Android?WindowManger實(shí)現(xiàn)桌面懸浮窗功能
如果想實(shí)現(xiàn)一個(gè)在桌面顯示的懸浮窗,用Dialog、PopupWindow、Toast等已經(jīng)不能實(shí)現(xiàn)了,他們基本都是在Activity之上顯示的,如果想實(shí)現(xiàn)在桌面顯示的懸浮窗效果,需要用到WindowManager來實(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);
通過上面的代碼就可以實(shí)現(xiàn)一個(gè)桌面懸浮窗功能了。
注意:在6.0以上,需要在Manifest.xml中聲明 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />權(quán)限并且在開啟懸浮窗時(shí)動(dòng)態(tài)判斷權(quán)限,如果沒有此權(quán)限需要跳到設(shè)置頁面去設(shè)置,看下官方文檔的說明:
分析
1、添加懸浮窗: 通過Context.getSystemService(Context.WINDOW_SERVICE)獲得一個(gè)WindowManager(以下簡稱VM), VM是外界訪問Window的入口,Activity、Dialog、Toast等其視圖都是依附在Window之上的,Window是View的直接管理者,VM繼承自ViewManager,其添加、刷新、刪除方法也是來自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)部類WindowManager.LayoutParams ,Window的各個(gè)屬性在這個(gè)內(nè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來標(biāo)識(shí)TYPE。 - LayoutParams.FLAGS
FLAGS表示Window的屬性,通過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)的WindowLayoutParams.FLAG_SHOW_WHEN_LOCKED: 此模式可以讓Window顯示在鎖屏的界面上 - LayoutParams.FORMAT 懸浮窗Window的背景格式,一般設(shè)置成
PixelFormat.TRANSPARENT透明即可 - LayoutParams.X & LayoutParams.Y 懸浮窗
Window在屏幕上的坐標(biāo)值,可以根據(jù)X&Y的值來刷新Window在屏幕上的位置 - LayoutParams.Width & LayoutParams.Height 懸浮窗
Window的寬度和高度
2、更新懸浮窗位置: 在View的OnTouchEvent中或OnTouch中更新layoutParams.x及layoutParams.y的值并通過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、刪除懸浮窗: 刪除比較簡單,直接調(diào)用windowManager.removeView(view)把view從Window中刪除即可。
問題
在6.0以上使用時(shí),需要?jiǎng)討B(tài)申請?jiān)搼腋〈皺?quán)限,如下:
//判斷有沒有懸浮窗權(quán)限,沒有去申請
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)限未開啟,請?jiān)谠O(shè)置中手動(dòng)打開");
return;
}
WindowController.getInstance().showThumbWindow();
break;
}
}通過Settings.canDrawOverlays(context)判斷是否有懸浮窗權(quán)限,如果沒有,跳轉(zhuǎn)到設(shè)置頁面去設(shè)置,并在onActivityResult ()中得到申請結(jié)果,看似很完美,但在實(shí)際測試中,發(fā)現(xiàn)在8.0以上的手機(jī)上有問題,即使在設(shè)置中同意了權(quán)限,8.0的手機(jī)Settings.canDrawOverlays(context)總是返回false,不過在關(guān)閉頁面重新調(diào)用此方法時(shí),又返回的true,感覺是有一定的延遲,google了一下,發(fā)現(xiàn)別人同樣遇到了這個(gè)問題,貌似已經(jīng)給google提交了bug單,可以看此博客: http://paskov.vmsoft-bg.com/settings-candrawoverlays-allays-returns-false-on-android-o/ ,不過博客中的解決方法用我的8.0手機(jī)(HUAWEI MATE10)依然不起作用,暫時(shí)還沒深入研究,有解決此問題的還希望不吝賜教。
以上例子的源碼地址:https://github.com/crazyqiang
參考
【1】developer.android.com/reference/a…
到此這篇關(guān)于Android WindowManger實(shí)現(xiàn)桌面懸浮窗功能的文章就介紹到這了,更多相關(guān)Android桌面懸浮窗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android使用PowerImageView實(shí)現(xiàn)播放強(qiáng)大的ImageView動(dòng)畫效果
今天我們就來編寫一個(gè)PowerImageView控件,讓它既能支持ImageView控件原生的所有功能,同時(shí)還可以播放GIF圖片2018-05-05
Android?studio實(shí)現(xiàn)簡單計(jì)算器的編寫
這篇文章主要為大家詳細(xì)介紹了Android?studio實(shí)現(xiàn)簡單計(jì)算器的編寫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Android如何優(yōu)雅的處理重復(fù)點(diǎn)擊
這篇文章主要介紹了Android如何優(yōu)雅的處理重復(fù)點(diǎn)擊,幫助大家更好的理解和學(xué)習(xí)使用Android開發(fā),感興趣的朋友可以了解下2021-03-03
Android使用ListView實(shí)現(xiàn)滾輪的動(dòng)畫效果實(shí)例
這篇文章主要介紹了Android使用ListView實(shí)現(xiàn)滾輪的動(dòng)畫效果實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
Android RecyclerView多類型布局卡片解決方案
這篇文章主要介紹了Android RecyclerView多類型布局卡片解決方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03
詳解Android如何實(shí)現(xiàn)不同大小的圓角
在開發(fā)過程中,設(shè)計(jì)常常會(huì)有一些比較炫酷的想法,比如兩邊不一樣大小的圓角啦,甚至四角的radius各不相同,對于這種情況我們該怎么實(shí)現(xiàn)呢,本文小編就和大家來聊聊,需要的朋友可以參考下2023-08-08

