Android?WindowManger實(shí)現(xiàn)桌面懸浮窗功能
如果想實(shí)現(xiàn)一個(gè)在桌面顯示的懸浮窗,用Dialog
、PopupWindow
、Toast
等已經(jīng)不能實(shí)現(xiàn)了,他們基本都是在Activity
之上顯示的,如果想實(shí)現(xiàn)在桌面顯示的懸浮窗效果,需要用到WindowManager
來(lái)實(shí)現(xiàn)了。
效果圖
使用WindowManager實(shí)現(xiàn)
- 添加一個(gè)懸浮窗:
sys_view = new SmallWindowView(mContext); sys_view.setText("50%"); sys_view.setOnTouchListener(this); windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); int screenWidth = 0, screenHeight = 0; if (windowManager != null) { //獲取屏幕的寬和高 Point point = new Point(); windowManager.getDefaultDisplay().getSize(point); screenWidth = point.x; screenHeight = point.y; layoutParams = new WindowManager.LayoutParams(); // layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; // layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.width = 200; layoutParams.height = 200; //設(shè)置type if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //26及以上必須使用TYPE_APPLICATION_OVERLAY @deprecated TYPE_PHONE layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } //設(shè)置flags layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; layoutParams.gravity = Gravity.START | Gravity.TOP; //背景設(shè)置成透明 layoutParams.format = PixelFormat.TRANSPARENT; layoutParams.x = screenWidth; layoutParams.y = screenHeight / 2; //將View添加到屏幕上 windowManager.addView(sys_view, layoutParams); }
- 更新懸浮窗位置:
windowManager.updateViewLayout(sys_view, layoutParams);
- 關(guān)閉懸浮窗:
windowManager.removeView(sys_view);
通過(guò)上面的代碼就可以實(shí)現(xiàn)一個(gè)桌面懸浮窗功能了。
注意:在6.0
以上,需要在Manifest.xml
中聲明 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
權(quán)限并且在開(kāi)啟懸浮窗時(shí)動(dòng)態(tài)判斷權(quán)限,如果沒(méi)有此權(quán)限需要跳到設(shè)置頁(yè)面去設(shè)置,看下官方文檔的說(shuō)明:
分析
1、添加懸浮窗: 通過(guò)Context.getSystemService(Context.WINDOW_SERVICE)
獲得一個(gè)WindowManager
(以下簡(jiǎn)稱(chēng)VM), VM
是外界訪問(wèn)Window
的入口,Activity
、Dialog
、Toast
等其視圖都是依附在Window
之上的,Window
是View
的直接管理者,VM
繼承自ViewManager
,其添加、刷新、刪除方法也是來(lái)自ViewManager
:
public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }
VM
有一個(gè)靜態(tài)內(nèi)部類(lèi)WindowManager.LayoutParams
,Window
的各個(gè)屬性在這個(gè)內(nèi)部類(lèi)中設(shè)置:
- LayoutParams.TYPE 如果
TargetSdkVersion<26
,那么可以直接使用LayoutParams.TYPE_PHONE
或者LayoutParams.TYPE_SYSTEM_ALERT
,在TargetSdkVersion>=26
時(shí),TYPE_PHONE
和TYPE_SYSTEM_ALERT
都已經(jīng)廢棄了,需要使用TYPE_APPLICATION_OVERLAY
來(lái)標(biāo)識(shí)TYPE
。 - LayoutParams.FLAGS
FLAGS
表示Window
的屬性,通過(guò)FLAGS
可以控制Window
的顯示特性,常用的幾個(gè)特性:LayoutParams.FLAG_NOT_TOUCH_MODAL
: 使用了此標(biāo)識(shí),可以將點(diǎn)擊事件傳遞到懸浮窗以外的區(qū)域,反之其他區(qū)域的Window
將接收不到事件。LayoutParams.FLAG_NOT_FOCUSABLE
: 表示懸浮窗Window
不需要獲取焦點(diǎn),也不需要獲取各種輸入事件,事件會(huì)直接傳遞給下層的具有焦點(diǎn)的Window
LayoutParams.FLAG_SHOW_WHEN_LOCKED
: 此模式可以讓Window
顯示在鎖屏的界面上 - LayoutParams.FORMAT 懸浮窗Window的背景格式,一般設(shè)置成
PixelFormat.TRANSPARENT
透明即可 - LayoutParams.X & LayoutParams.Y 懸浮窗
Window
在屏幕上的坐標(biāo)值,可以根據(jù)X&Y
的值來(lái)刷新Window
在屏幕上的位置 - LayoutParams.Width & LayoutParams.Height 懸浮窗
Window
的寬度和高度
2、更新懸浮窗位置: 在View
的OnTouchEvent
中或OnTouch
中更新layoutParams.x
及layoutParams.y
的值并通過(guò)windowManager.updateViewLayout()
重新設(shè)置懸浮窗Window在屏幕中的位置,如下:
@Override public boolean onTouch(View v, MotionEvent event) { int mInScreenX = (int) event.getRawX(); int mInScreenY = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = (int) event.getRawX(); mLastY = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: layoutParams.x += mInScreenX - mLastX; layoutParams.y += mInScreenY - mLastY; mLastX = mInScreenX; mLastY = mInScreenY; windowManager.updateViewLayout(sys_view, layoutParams); break; case MotionEvent.ACTION_UP: break; } return true; }
3、刪除懸浮窗: 刪除比較簡(jiǎn)單,直接調(diào)用windowManager.removeView(view)
把view
從Window
中刪除即可。
問(wèn)題
在6.0以上
使用時(shí),需要?jiǎng)討B(tài)申請(qǐng)?jiān)搼腋〈皺?quán)限,如下:
//判斷有沒(méi)有懸浮窗權(quán)限,沒(méi)有去申請(qǐng) if(!Settings.canDrawOverlays(context)){ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName())); context.startActivityForResult(intent, REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_CODE: if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; if (!WindowUtil.canOverDraw(this)) { toast("懸浮窗權(quán)限未開(kāi)啟,請(qǐng)?jiān)谠O(shè)置中手動(dòng)打開(kāi)"); return; } WindowController.getInstance().showThumbWindow(); break; } }
通過(guò)Settings.canDrawOverlays(context)
判斷是否有懸浮窗權(quán)限,如果沒(méi)有,跳轉(zhuǎn)到設(shè)置頁(yè)面去設(shè)置,并在onActivityResult ()
中得到申請(qǐng)結(jié)果,看似很完美,但在實(shí)際測(cè)試中,發(fā)現(xiàn)在8.0以上的手機(jī)上有問(wèn)題,即使在設(shè)置中同意了權(quán)限,8.0的手機(jī)Settings.canDrawOverlays(context)
總是返回false
,不過(guò)在關(guān)閉頁(yè)面重新調(diào)用此方法時(shí),又返回的true
,感覺(jué)是有一定的延遲,google
了一下,發(fā)現(xiàn)別人同樣遇到了這個(gè)問(wèn)題,貌似已經(jīng)給google
提交了bug
單,可以看此博客: http://paskov.vmsoft-bg.com/settings-candrawoverlays-allays-returns-false-on-android-o/
,不過(guò)博客中的解決方法用我的8.0手機(jī)
(HUAWEI MATE10
)依然不起作用,暫時(shí)還沒(méi)深入研究,有解決此問(wèn)題的還希望不吝賜教。
以上例子的源碼地址:https://github.com/crazyqiang
參考
【1】developer.android.com/reference/a…
到此這篇關(guān)于Android WindowManger實(shí)現(xiàn)桌面懸浮窗功能的文章就介紹到這了,更多相關(guān)Android桌面懸浮窗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 斷點(diǎn)續(xù)傳原理以及實(shí)現(xiàn)
這篇文章主要介紹了Android 斷點(diǎn)續(xù)傳原理以及實(shí)現(xiàn)的相關(guān)資料,這里對(duì)斷點(diǎn)續(xù)傳原理進(jìn)行了詳細(xì)介紹,需要的朋友可以參考下2016-12-12Android 自動(dòng)補(bǔ)全提示輸入AutoCompleteTextView、 MultiAutoCompleteTextV
本文主要介紹了Android自動(dòng)補(bǔ)全提示輸入AutoCompleteTextView、 MultiAutoCompleteTextView,具有一定的參考作用,下面跟著小編一起來(lái)看下吧2017-01-01Kotlin?協(xié)程異步熱數(shù)據(jù)流的設(shè)計(jì)與使用講解
這篇文章主要為大家介紹了Kotlin?協(xié)程協(xié)程異步熱數(shù)據(jù)流的設(shè)計(jì)與使用講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Android自定義左右或上下滑動(dòng)翻頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了Android自定義左右或上下滑動(dòng)翻頁(yè)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android使用phonegap從相冊(cè)里面獲取照片(代碼分享)
本文主要介紹了使用phonegap從相冊(cè)里面獲取照片的實(shí)現(xiàn)方法代碼。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-03-03Android基于OpenCV實(shí)現(xiàn)Harris角點(diǎn)檢測(cè)
角點(diǎn)就是極值點(diǎn),即在某方面屬性特別突出的點(diǎn)。當(dāng)然,你可以自己定義角點(diǎn)的屬性(設(shè)置特定熵值進(jìn)行角點(diǎn)檢測(cè))。角點(diǎn)可以是兩條線(xiàn)的交叉處,也可以是位于相鄰的兩個(gè)主要方向不同的事物上的點(diǎn)。本文介紹如何基于OpenCV實(shí)現(xiàn)Harris角點(diǎn)檢測(cè)2021-06-06Android Studio綁定下拉框數(shù)據(jù)詳解
這篇文章主要為大家詳細(xì)介紹了Android Studio綁定下拉框數(shù)據(jù),Android Studio綁定網(wǎng)絡(luò)JSON數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Android實(shí)現(xiàn)將已發(fā)送的短信寫(xiě)入短信數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Android實(shí)現(xiàn)將已發(fā)送的短信寫(xiě)入短信數(shù)據(jù)庫(kù)的方法,是Android手機(jī)開(kāi)發(fā)常見(jiàn)的技巧,需要的朋友可以參考下2014-09-09淺談Android單元測(cè)試的作用以及簡(jiǎn)單示例
本篇文章主要介紹了淺談Android單元測(cè)試的作用以及簡(jiǎn)單示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08