Android WindowManger实现桌面悬浮窗功能
作者:_小马快跑 发布时间:2023-08-01 02:16:10
如果想实现一个在桌面显示的悬浮窗,用Dialog
、PopupWindow
、Toast
等已经不能实现了,他们基本都是在Activity
之上显示的,如果想实现在桌面显示的悬浮窗效果,需要用到WindowManager
来实现了。
效果图
使用WindowManager实现
添加一个悬浮窗:
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;
//设置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;
}
//设置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;
//背景设置成透明
layoutParams.format = PixelFormat.TRANSPARENT;
layoutParams.x = screenWidth;
layoutParams.y = screenHeight / 2;
//将View添加到屏幕上
windowManager.addView(sys_view, layoutParams);
}
更新悬浮窗位置:
windowManager.updateViewLayout(sys_view, layoutParams);
关闭悬浮窗:
windowManager.removeView(sys_view);
通过上面的代码就可以实现一个桌面悬浮窗功能了。
注意:在6.0
以上,需要在Manifest.xml
中声明 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
权限并且在开启悬浮窗时动态判断权限,如果没有此权限需要跳到设置页面去设置,看下官方文档的说明:
分析
1、添加悬浮窗: 通过Context.getSystemService(Context.WINDOW_SERVICE)
获得一个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
有一个静态内部类WindowManager.LayoutParams
,Window
的各个属性在这个内部类中设置:
LayoutParams.TYPE 如果
TargetSdkVersion<26
,那么可以直接使用LayoutParams.TYPE_PHONE
或者LayoutParams.TYPE_SYSTEM_ALERT
,在TargetSdkVersion>=26
时,TYPE_PHONE
和TYPE_SYSTEM_ALERT
都已经废弃了,需要使用TYPE_APPLICATION_OVERLAY
来标识TYPE
。LayoutParams.FLAGS
FLAGS
表示Window
的属性,通过FLAGS
可以控制Window
的显示特性,常用的几个特性:LayoutParams.FLAG_NOT_TOUCH_MODAL
: 使用了此标识,可以将点击事件传递到悬浮窗以外的区域,反之其他区域的Window
将接收不到事件。LayoutParams.FLAG_NOT_FOCUSABLE
: 表示悬浮窗Window
不需要获取焦点,也不需要获取各种输入事件,事件会直接传递给下层的具有焦点的Window
LayoutParams.FLAG_SHOW_WHEN_LOCKED
: 此模式可以让Window
显示在锁屏的界面上LayoutParams.FORMAT 悬浮窗Window的背景格式,一般设置成
PixelFormat.TRANSPARENT
透明即可LayoutParams.X & LayoutParams.Y 悬浮窗
Window
在屏幕上的坐标值,可以根据X&Y
的值来刷新Window
在屏幕上的位置LayoutParams.Width & LayoutParams.Height 悬浮窗
Window
的宽度和高度
2、更新悬浮窗位置: 在View
的OnTouchEvent
中或OnTouch
中更新layoutParams.x
及layoutParams.y
的值并通过windowManager.updateViewLayout()
重新设置悬浮窗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、删除悬浮窗: 删除比较简单,直接调用windowManager.removeView(view)
把view
从Window
中删除即可。
问题
在6.0以上
使用时,需要动态申请该悬浮窗权限,如下:
//判断有没有悬浮窗权限,没有去申请
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("悬浮窗权限未开启,请在设置中手动打开");
return;
}
WindowController.getInstance().showThumbWindow();
break;
}
}
通过Settings.canDrawOverlays(context)
判断是否有悬浮窗权限,如果没有,跳转到设置页面去设置,并在onActivityResult ()
中得到申请结果,看似很完美,但在实际测试中,发现在8.0以上的手机上有问题,即使在设置中同意了权限,8.0的手机Settings.canDrawOverlays(context)
总是返回false
,不过在关闭页面重新调用此方法时,又返回的true
,感觉是有一定的延迟,google
了一下,发现别人同样遇到了这个问题,貌似已经给google
提交了bug
单,可以看此博客: http://paskov.vmsoft-bg.com/settings-candrawoverlays-allays-returns-false-on-android-o/
,不过博客中的解决方法用我的8.0手机
(HUAWEI MATE10
)依然不起作用,暂时还没深入研究,有解决此问题的还希望不吝赐教。
以上例子的源码地址:https://github.com/crazyqiang
参考
【1】developer.android.com/reference/a…
来源:https://juejin.cn/post/7226911455878086715
猜你喜欢
- flutter material widget组件之信息展示组件,供大家参考,具体内容如下widget分为两类:widgets librar
- iOS定位 - 普通定位(没有地图) - 反地理编码(得到具体位置),下面通过代码给大家详解,代码如下:#import <CoreLo
- Visual Studio 2022 默认.net framework4.8,而4.6~4.7版本的.net framework可以通过方法
- 本文实例讲述了Android编程实现WebView添加进度条的方法。分享给大家供大家参考,具体如下:标准的XML界面<?xml ver
- 本文实例讲述了C#获取网页源代码的方法。分享给大家供大家参考。具体如下:public string GetPageHTML(string u
- 序言在flutter开发中,我们使用 bloc 框架,基于状态变更进行响应式开发。本篇文章,小轰将 bloc 核心业务块进行拆解简化,聊一聊
- 简介官方API文档Scaffold的of方法说明有说明调用Scaffold.of方法是在Scallfold的子组件的Build方法中,也就是
- 本文实例讲述了C++语言实现线性表之链表实现方法。分享给大家供大家参考。具体分析如下:插入、删除结点的代码有点多,但这样提高了代码的可读性,
- Java 使用getClass().getResourceAsStream()方法获取资源之前想获取一个资源文件做一些处理,使用getCla
- 前言我们在 页面切换转场动画,英雄救场更有趣!介绍了 Hero 动画效果,使用 Hero 用于转场能够提供非常不错的体验。既然称之
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- Android Studio 打包 jar 及 aar 包创建工程 New -> Module -> Library在gradl
- 概述LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量,值得注意的4个
- 多选和单选的不同之处单选的时候,选中一个就可以直接把结果返回,因此本身底部弹窗无需状态管理。但到多选的时候,需要知道当前选中的选项,有选项被
- 本文实例讲述了java实现的简单猜数字游戏代码。分享给大家供大家参考。具体代码如下:import java.util.InputMismat
- 免责声明:本教程所有资源均来源于网络;仅用于学习交流,请勿用于任何商业行为;如需要,请使用正版授权;侵权联删。推荐最新 IntelliJ I
- 在spring中有很多以XXXAware命名的接口,很多人也不清楚这些接口都是做什么用的,这篇文章将描述常用的一些接口。一,Applicat
- 背景最近好几个项目在运行过程中客户都提出文件上传大小的限制能否设置的大一些,用户经常需要上传好几个G的资料文件,如图纸,视频等,并且需要在上
- Note:这篇文章是基于Android Studio 3.01版本的,NDK是R16。step1:创建一个包含C++的项目其他默认就可以了。
- 机器学习 机器学习的目的是把数据转换成信息。 机器学习通过从数据里提取规则或模式来把数据转成信息。 人脸识别 人脸识别通过级联分类器对特征的