软件编程
位置:首页>> 软件编程>> Android编程>> Android WindowManger实现桌面悬浮窗功能

Android WindowManger实现桌面悬浮窗功能

作者:_小马快跑  发布时间:2023-08-01 02:16:10 

标签:Android,WindowManger,悬浮窗

如果想实现一个在桌面显示的悬浮窗,用DialogPopupWindowToast等已经不能实现了,他们基本都是在Activity之上显示的,如果想实现在桌面显示的悬浮窗效果,需要用到WindowManager来实现了。

效果图

Android WindowManger实现桌面悬浮窗功能

使用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" />权限并且在开启悬浮窗时动态判断权限,如果没有此权限需要跳到设置页面去设置,看下官方文档的说明:

Android WindowManger实现桌面悬浮窗功能

分析

1、添加悬浮窗: 通过Context.getSystemService(Context.WINDOW_SERVICE)获得一个WindowManager(以下简称VM), VM是外界访问Window的入口,ActivityDialogToast等其视图都是依附在Window之上的,WindowView的直接管理者,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.LayoutParamsWindow的各个属性在这个内部类中设置:

  • LayoutParams.TYPE 如果TargetSdkVersion<26 ,那么可以直接使用LayoutParams.TYPE_PHONE或者LayoutParams.TYPE_SYSTEM_ALERT,在TargetSdkVersion>=26时,TYPE_PHONETYPE_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、更新悬浮窗位置: 在ViewOnTouchEvent中或OnTouch中更新layoutParams.xlayoutParams.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)viewWindow中删除即可。

问题

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&hellip;

来源:https://juejin.cn/post/7226911455878086715

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com