0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

鴻蒙實(shí)戰(zhàn)開(kāi)發(fā):【實(shí)現(xiàn)應(yīng)用懸浮窗】

jf_46214456 ? 來(lái)源:jf_46214456 ? 作者:jf_46214456 ? 2024-04-03 22:18 ? 次閱讀

如果你要做的是系統(tǒng)級(jí)別的懸浮窗,就需要判斷是否具備懸浮窗權(quán)限。然而這又不是一個(gè)標(biāo)準(zhǔn)的動(dòng)態(tài)權(quán)限,你需要兼容各種奇葩機(jī)型的懸浮窗權(quán)限判斷,下面的代碼來(lái)自于某著名開(kāi)源庫(kù):EasyFloat[1] 。

fun checkPermission(context: Context): Boolean =
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) when {
    RomUtils.checkIsHuaweiRom() - > huaweiPermissionCheck(context)
    RomUtils.checkIsMiuiRom() - > miuiPermissionCheck(context)
    RomUtils.checkIsOppoRom() - > oppoROMPermissionCheck(context)
    RomUtils.checkIsMeizuRom() - > meizuPermissionCheck(context)
    RomUtils.checkIs360Rom() - > qikuPermissionCheck(context)
            else - > true
} else commonROMPermissionCheck(context)

private fun commonROMPermissionCheck(context: Context): Boolean =
        if (RomUtils.checkIsMeizuRom()) meizuPermissionCheck(context) else {
            var result = true
            if (Build.VERSION.SDK_INT >= 23) try {
                val clazz = Settings::class.java
                val canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context::class.java)
                result = canDrawOverlays.invoke(null, context) as Boolean
            } catch (e: Exception) {
                Log.e(TAG, Log.getStackTraceString(e))
            }
            result
        }

如果你要做的是應(yīng)用內(nèi)的全局懸浮窗,那么對(duì)不起,不支持,自己想辦法。普遍的做法是在根布局 DecorView 直接塞進(jìn)去。

遙遙領(lǐng)先qr23.cn/AKFP8k獲取

.png

或者加mau123789是v直接領(lǐng)??!

鴻蒙上實(shí)現(xiàn)懸浮窗相對(duì)就要簡(jiǎn)單的多。

對(duì)于系統(tǒng)級(jí)別彈窗,仍然需要權(quán)限,但也不至于那么麻煩的適配。

對(duì)于應(yīng)用內(nèi)全局彈出,鴻蒙提供了 應(yīng)用子窗口 可以直接實(shí)現(xiàn)。

本文主要介紹如何利用應(yīng)用子窗口實(shí)現(xiàn)應(yīng)用內(nèi)全局懸浮窗。

創(chuàng)建應(yīng)用子窗口需要先拿到窗口管理器 WindowStage 對(duì)象,在 EntryAbility.onWindowStageCreate() 回調(diào)中取。

FloatManager.init(windowStage)

init(windowStage: window.WindowStage) {
  this.windowStage_ = windowStage
}

然后通過(guò) WindowStage.createSubWindow() 創(chuàng)建子窗口。

// 創(chuàng)建子窗口
showSubWindow() {
    if (this.windowStage_ == null) {
        Log.error(TAG, 'Failed to create the subwindow. Cause: windowStage_ is null');
    } else {
        this.windowStage_.createSubWindow("HarmonyWorld", (err: BusinessError, data) = > {
            ...
            this.sub_windowClass = data;
            // 子窗口創(chuàng)建成功后,設(shè)置子窗口的位置、大小及相關(guān)屬性等
            // moveWindowTo 和 resize 都可以重復(fù)調(diào)用,實(shí)現(xiàn)拖拽效果
            this.sub_windowClass.moveWindowTo(this.locationX, this.locationY, (err: BusinessError) = > {
                ...
            });
            this.sub_windowClass.resize(this.size, this.size, (err: BusinessError) = > {
                ...
            });
            // 給子窗口設(shè)置內(nèi)容
            this.sub_windowClass.setUIContent("pages/float/FloatPage", (err: BusinessError) = > {
                ...
                // 顯示子窗口。
                (this.sub_windowClass as window.Window).showWindow((err: BusinessError) = > {
                    ...
                    // 設(shè)置透明背景
                    data.setWindowBackgroundColor("#00000000")
                });
            });
        })
    }
}

這樣就可以在指定位置顯示指定大小的的懸浮窗了。

然后再接著完善手勢(shì)拖動(dòng)和點(diǎn)擊事件。

既要監(jiān)聽(tīng)拖動(dòng),又要監(jiān)聽(tīng)手勢(shì),就需要通過(guò) GestoreGroup,并把設(shè)置模式設(shè)置為 互斥識(shí)別

@Entry
@Component
export struct FloatPage {
  private context = getContext(this) as common.UIAbilityContext

  build() {
    Column() {
      Image($r('app.media.mobile_dev'))
        .width('100%')
        .height('100%')
    }
    .gesture(
      GestureGroup(GestureMode.Exclusive,
        // 監(jiān)聽(tīng)拖動(dòng)
        PanGesture()
          .onActionUpdate((event: GestureEvent | undefined) = > {
            if (event) {
              // 更新懸浮窗位置
              FloatManager.updateLocation(event.offsetX, event.offsetY)
            }
          }),
        // 監(jiān)聽(tīng)點(diǎn)擊
        TapGesture({ count: 1 })
          .onAction(() = > {
             router.pushUrl(...)
          }))
    )
  }
}

在拖動(dòng)手勢(shì) PanGestureonActionUpdate() 回調(diào)中,可以實(shí)時(shí)拿到拖動(dòng)的距離,然后通過(guò) Window.moveWindowTo() 就可以實(shí)時(shí)更新懸浮窗的位置了。

updateLocation(offSetX: number, offsetY: number) {
    if (this.sub_windowClass != null) {
        this.locationX = this.locationX + offSetX
        this.locationY = this.locationY + offsetY
        this.sub_windowClass.moveWindowTo(this.locationX, this.locationY, (err: BusinessError) = > {
            ......
        });
    }
}

在點(diǎn)擊手勢(shì) TapGesture中,我的需求是路由到指定頁(yè)面,直接調(diào)用 router.pushUrl()??此坪苷5恼{(diào)用,在這里確得到了意想不到的結(jié)果。

發(fā)生頁(yè)面跳轉(zhuǎn)的并不是預(yù)期中的應(yīng)用主窗口,而是應(yīng)用子窗口。

把問(wèn)題拋到群里之后,得到了群友的熱心解答。

每個(gè) Window 對(duì)應(yīng)自己的 UIContext,UIContext 持有自己的 Router ,所以應(yīng)用主窗口和應(yīng)用子窗口的 Router 是相互獨(dú)立的。

那么,問(wèn)題就變成了如何在子窗口中讓主窗口進(jìn)行路由跳轉(zhuǎn)?通過(guò) EventHub 或者 emitter 都可以。emiiter 可以跨線程,這里并不需要,EventHub 寫起來(lái)更簡(jiǎn)單。我們?cè)邳c(diǎn)擊手勢(shì)中發(fā)送事件:

TapGesture({ count: 1 })
  .onAction(() = > {
      this.context.eventHub.emit("event_click_float")
  })

EntryAbility 中訂閱事件:

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    eventHub.on("event_click_float", () = > {
      if (this.mainRouter) {
        this.mainRouter.pushUrl(...)
      }
    })
}

這里的 mainRouter 我們可以提前在主 Window 調(diào)用 loadContent() 之后獲?。?/p>

windowStage.loadContent(pages/Index', (err, data) = > {
  this.mainRouter = this.windowClass!.getUIContext().getRouter()
});

最后還有一個(gè)小細(xì)節(jié),如果在拖動(dòng)懸浮窗之后,再使用系統(tǒng)的返回手勢(shì),按照預(yù)期應(yīng)該是主窗口的頁(yè)面返回,但這時(shí)候焦點(diǎn)在子窗口,主窗口并不會(huì)響應(yīng)返回手勢(shì)。

我們需要在子窗口承載的 Page 頁(yè)面監(jiān)聽(tīng) onBackPress(),并通過(guò) EventHub 通知主窗口。

onBackPress(): boolean | void {
    this.context.eventHub.emit("float_back")
  }

主窗口接收到通知后,調(diào)用 mainRouter.back 。

eventHub.on("clickFloat", () = > {
  if (this.mainRouter) {
    this.mainRouter.back()
  }
})

應(yīng)用內(nèi)全局,可拖拽的懸浮窗就完成了。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2303

    瀏覽量

    42693
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    鴻蒙Flutter實(shí)戰(zhàn):11-使用 Flutter SDK 3.22.0

    # 使用 Flutter SDK 3.22.0 ## SDK 安裝 參考[鴻蒙Flutter實(shí)戰(zhàn):01-搭建開(kāi)發(fā)環(huán)境]文章的說(shuō)明,首先安裝 Flutter SDK 3.22.0。 目前
    發(fā)表于 11-01 15:03

    鴻蒙Flutter實(shí)戰(zhàn):08-如何調(diào)試代碼

    # 鴻蒙Flutter實(shí)戰(zhàn):如何調(diào)試代碼 ## 1.環(huán)境搭建 參考文章[鴻蒙Flutter實(shí)戰(zhàn):01-搭建開(kāi)發(fā)環(huán)境](https://g
    發(fā)表于 10-23 16:29

    鴻蒙Flutter實(shí)戰(zhàn):07混合開(kāi)發(fā)

    # 鴻蒙Flutter實(shí)戰(zhàn):混合開(kāi)發(fā) 鴻蒙Flutter混合開(kāi)發(fā)主要有兩種形式。 ## 1.基于har 將flutter module
    發(fā)表于 10-23 16:00

    鴻蒙Flutter實(shí)戰(zhàn):05-使用第三方插件

    # 鴻蒙Flutter 實(shí)戰(zhàn):使用第三方插件 在鴻蒙Flutter開(kāi)發(fā)中,如果涉及到使用原生功能,就要使用插件。使用插件有兩種方式,一種是自己編寫原生ArkTS代碼,在Dart側(cè)調(diào)用
    發(fā)表于 10-22 21:54

    36歲了還有必要轉(zhuǎn)行鴻蒙開(kāi)發(fā)嗎?

    開(kāi)發(fā)員的職業(yè)前景光明,提供了大量的創(chuàng)新機(jī)會(huì),助力實(shí)現(xiàn)個(gè)人職業(yè)目標(biāo)。 最后 如果大家覺(jué)得這篇內(nèi)容對(duì)學(xué)習(xí)鴻蒙開(kāi)發(fā)有幫助,我想邀請(qǐng)大家?guī)臀胰齻€(gè)小忙: 點(diǎn)贊,轉(zhuǎn)發(fā),有你們的 『點(diǎn)贊和評(píng)論』,
    發(fā)表于 05-09 17:01

    HarmonyOS實(shí)戰(zhàn)開(kāi)發(fā)-如何通過(guò)BlendMode屬性來(lái)實(shí)現(xiàn)掛件和圖片的混合

    ||---BlendModeView.ets // 視圖層-應(yīng)用主頁(yè)面 模塊依賴 本實(shí)例依賴common模塊來(lái)實(shí)現(xiàn)日志的打印、資源 的調(diào)用、依賴動(dòng)態(tài)路由模塊來(lái)實(shí)現(xiàn)頁(yè)面的動(dòng)態(tài)加載。 最后 如果大家覺(jué)得這篇內(nèi)容對(duì)學(xué)習(xí)鴻蒙
    發(fā)表于 05-07 14:45

    OpenHarmony實(shí)戰(zhàn)開(kāi)發(fā)-如何實(shí)現(xiàn)窗口開(kāi)發(fā)概述

    你們的 『點(diǎn)贊和評(píng)論』,才是我創(chuàng)造的動(dòng)力。 關(guān)注小編,同時(shí)可以期待后續(xù)文章ing?,不定期分享原創(chuàng)知識(shí)。 更多鴻蒙最新技術(shù)知識(shí)點(diǎn),請(qǐng)關(guān)注作者博客:鴻蒙實(shí)戰(zhàn)經(jīng)驗(yàn)分享:鴻蒙基礎(chǔ)入門
    發(fā)表于 05-06 14:29

    鴻蒙OS開(kāi)發(fā)學(xué)習(xí):【尺寸適配實(shí)現(xiàn)

    鴻蒙開(kāi)發(fā)中,尺寸適配是一個(gè)重要的概念,它可以幫助我們?cè)诓煌聊怀叽绲脑O(shè)備上正確顯示和布局我們的應(yīng)用程序。本文將介紹如何在鴻蒙開(kāi)發(fā)實(shí)現(xiàn)尺寸
    的頭像 發(fā)表于 04-10 16:05 ?1648次閱讀
    <b class='flag-5'>鴻蒙</b>OS<b class='flag-5'>開(kāi)發(fā)</b>學(xué)習(xí):【尺寸適配<b class='flag-5'>實(shí)現(xiàn)</b>】

    鴻蒙OS元服務(wù)開(kāi)發(fā):【(Stage模型)設(shè)置懸浮

    懸浮可以在已有的任務(wù)基礎(chǔ)上,創(chuàng)建一個(gè)始終在前臺(tái)顯示的窗口。即使創(chuàng)建懸浮的任務(wù)退至后臺(tái),懸浮
    的頭像 發(fā)表于 04-03 15:32 ?1054次閱讀
    <b class='flag-5'>鴻蒙</b>OS元服務(wù)<b class='flag-5'>開(kāi)發(fā)</b>:【(Stage模型)設(shè)置<b class='flag-5'>懸浮</b><b class='flag-5'>窗</b>】

    鴻蒙OS開(kāi)發(fā)實(shí)戰(zhàn):【懸浮窗口】

    懸浮視圖或者窗體,在Android和iOS兩大移動(dòng)平臺(tái)均有使用,HarmonyOS 也實(shí)現(xiàn)了此功能,如下為大家分享一下效果。
    的頭像 發(fā)表于 03-28 20:39 ?978次閱讀
    <b class='flag-5'>鴻蒙</b>OS<b class='flag-5'>開(kāi)發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b>:【<b class='flag-5'>懸浮</b>窗口】

    深圳市24年,實(shí)現(xiàn)鴻蒙原生應(yīng)用數(shù)占全國(guó)總量10%以上

    行動(dòng)計(jì)劃》 深圳市提出在鴻蒙原生應(yīng)用發(fā)展上的具體目標(biāo):在2024年內(nèi)實(shí)現(xiàn)深圳市鴻蒙原生應(yīng)用數(shù)量占全國(guó)總量10%以上;深圳市主要垂域實(shí)現(xiàn)鴻蒙
    發(fā)表于 03-04 21:42

    鴻蒙實(shí)戰(zhàn)項(xiàng)目開(kāi)發(fā):【短信服務(wù)】

    、OpenHarmony 多媒體技術(shù)、Napi組件、OpenHarmony內(nèi)核、Harmony南向開(kāi)發(fā)、鴻蒙項(xiàng)目實(shí)戰(zhàn)等等)鴻蒙(Harmony NEXT) 技術(shù)知識(shí)點(diǎn) 如果你是一名An
    發(fā)表于 03-03 21:29

    鴻蒙應(yīng)用/元服務(wù)開(kāi)發(fā)-窗口(Stage模型)設(shè)置懸浮

    一、設(shè)置懸浮說(shuō)明 懸浮可以在已有的任務(wù)基礎(chǔ)上,創(chuàng)建一個(gè)始終在前臺(tái)顯示的窗口。即使創(chuàng)建懸浮
    發(fā)表于 02-04 14:05

    使用 Taro 開(kāi)發(fā)鴻蒙原生應(yīng)用 —— 快速上手,鴻蒙應(yīng)用開(kāi)發(fā)指南

    隨著鴻蒙系統(tǒng)的不斷完善,許多應(yīng)用廠商都希望將自己的應(yīng)用移植到鴻蒙平臺(tái)上。最近,Taro 發(fā)布了 v4.0.0-beta.x 版本,支持使用 Taro 快速開(kāi)發(fā)鴻蒙原生應(yīng)用,也可將現(xiàn)有的
    的頭像 發(fā)表于 02-02 16:09 ?803次閱讀
    使用 Taro <b class='flag-5'>開(kāi)發(fā)</b><b class='flag-5'>鴻蒙</b>原生應(yīng)用 —— 快速上手,<b class='flag-5'>鴻蒙</b>應(yīng)用<b class='flag-5'>開(kāi)發(fā)</b>指南

    鴻蒙基礎(chǔ)開(kāi)發(fā)實(shí)戰(zhàn)-(ArkTS)像素轉(zhuǎn)換

    的使用。通過(guò)像素轉(zhuǎn)換案例,向開(kāi)發(fā)者講解了如何使用像素單位設(shè)置組件的尺寸、字體的大小以及不同像素單位之間的轉(zhuǎn)換方法。更多鴻蒙4.0的學(xué)習(xí),可以前往主頁(yè)學(xué)習(xí)或前往《鴻蒙4.0開(kāi)發(fā)學(xué)習(xí)目錄》
    發(fā)表于 01-11 16:53