你的位置:首页 > 操作系统

[操作系统]简易的可拖动的桌面悬浮窗效果Demo


首先,我们需要知道,悬浮窗分为两种:Activity级别的悬浮窗,系统级别的悬浮窗

Activity级别的悬浮窗跟随所属Activity的生命周期而变化,而系统级别的悬浮窗则可以脱离Activity而存在。

 

由此可知,要实现360手机卫士那样的悬浮窗效果,就需要使用系统级别的悬浮窗

 

下面学习实现桌面悬浮窗效果的代码步骤:

Demo描述,悬浮窗为一个ImageView ,可以在桌面 ,任意应用,锁屏上方任意移动

 

1、配置清单文件AndroidManifest.

  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

 

2、开始Activity代码的编写

 先看成员变量:

  private WindowManager.LayoutParams lp = new WindowManager.LayoutParams();  private static WindowManager windowManager;  private static ImageView imageView;

 onCreate()方法:

获取WindwoManager对象,该对象是系统级别的

windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);

使用WindowManager可以显示在其他应用最上层,甚至手机桌面最上层显示窗口。

 

3、添加一个UI空间,作为悬浮窗的内容 ,当然Demo是一个ImageView作为悬浮窗内容,实际项目中就需要用复杂View,ViewGroup来扩展功能了

     
     //注意,悬浮窗只有一个,而当打开应用的时候才会产生悬浮窗,所以要判断悬浮窗是否已经存在,
     if (imageView != null){ windowManager.removeView(imageView); } // 使用Application context 创建UI控件,避免Activity销毁导致上下文出现问题,因为现在的悬浮窗是系统级别的,不依赖与Activity存在    imageView = new ImageView(getApplicationContext()); imageView.setImageResource(R.mipmap.normal);

 

4、设置系统级别的悬浮窗的参数,保证悬浮窗悬在手机桌面上

     lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT         |WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;    lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE         |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

//TYPE_SYSTEM_ALERT 系统提示,它总是出现在应用程序窗口之上
//TYPE_SYSTEM_OVERLAY 系统顶层窗口。显示在其他一切内容之上。此窗口不能获得输入焦点,否则影响锁屏
// FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口

关于 WindowManager.LayoutParams 的详解 请参考:Android中WindowManager.LayoutParams类详解

5、悬浮窗默认显示的位置
 lp.gravity = Gravity.LEFT|Gravity.TOP; //显示在屏幕左上角

 

6、悬浮窗相对5默认位置的位置差和悬浮窗宽高设置

     //显示位置与指定位置的相对位置差    lp.x = 0;    lp.y = 0;    //悬浮窗的宽高    lp.width = WindowManager.LayoutParams.WRAP_CONTENT;    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;

 

7、设置悬浮窗背景透明

lp.format = PixelFormat.TRANSPARENT;

 

8、将悬浮窗添加到WindowManager对象中

 windowManager.addView(imageView,lp);

 

9.设置悬浮窗的响应事件

 这里为移动悬浮窗操作,可以自己扩展添加点击等响应事件

imageView.setOnTouchListener(new View.OnTouchListener() {      private float lastX; //上一次位置的X.Y坐标      private float lastY;      private float nowX; //当前移动位置的X.Y坐标      private float nowY;      private float tranX; //悬浮窗移动位置的相对值      private float tranY;      @Override      public boolean onTouch(View v, MotionEvent event) {        boolean ret = false;        switch (event.getAction()){          case MotionEvent.ACTION_DOWN:            // 获取按下时的X,Y坐标            lastX = event.getRawX();            lastY = event.getRawY();            ret = true;            break;          case MotionEvent.ACTION_MOVE:            // 获取移动时的X,Y坐标            nowX = event.getRawX();            nowY = event.getRawY();            // 计算XY坐标偏移量            tranX = nowX - lastX;            tranY = nowY - lastY;            // 移动悬浮窗            lp.x += tranX;            lp.y += tranY;            //更新悬浮窗位置            windowManager.updateViewLayout(imageView,lp);            //记录当前坐标作为下一次计算的上一次移动的位置坐标            lastX = nowX;            lastY = nowY;            break;          case MotionEvent.ACTION_UP:            break;        }        return ret;      }    });


10、扩展移除悬浮窗功能

11、效果图:


完整代码:
注意添加权限!!!
 1 package com.xqx.window.app; 2  3 import android.app.Activity; 4 import android.graphics.PixelFormat; 5 import android.os.Bundle; 6 import android.view.*; 7 import android.widget.ImageView; 8  9 /** 10  * 系统级别悬浮窗,可以在手机桌面上显示的悬浮窗 11 */ 12 public class FloatWindowActivity extends Activity { 13  14   private WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); 15   private static WindowManager windowManager; 16   private static ImageView imageView; 17  18   @Override 19   protected void onCreate(Bundle savedInstanceState) { 20     super.onCreate(savedInstanceState); 21     setContentView(R.layout.activity_float_window); 22  23     // 1、获取系统级别的WindowManager 24     windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE); 25  26     // 判断UI控件是否存在,存在则移除,确保开启任意次应用都只有一个悬浮窗 27     if (imageView != null){ 28       windowManager.removeView(imageView); 29     } 30     // 2、使用Application context 创建UI控件,避免Activity销毁导致上下文出现问题 31     imageView = new ImageView(getApplicationContext()); 32     imageView.setImageResource(R.mipmap.normal); 33  34  35     // 3、设置系统级别的悬浮窗的参数,保证悬浮窗悬在手机桌面上 36     // 系统级别需要指定type 属性 37     // TYPE_SYSTEM_ALERT 允许接收事件 38     // TYPE_SYSTEM_OVERLAY 悬浮在系统上 39     // 注意清单文件添加权限 40  41     //系统提示。它总是出现在应用程序窗口之上。 42     lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT 43          |WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; 44  45     // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口 46     // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题 47     lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 48          |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 49  50     //悬浮窗默认显示的位置 51     lp.gravity = Gravity.LEFT|Gravity.TOP; 52     //显示位置与指定位置的相对位置差 53     lp.x = 0; 54     lp.y = 0; 55     //悬浮窗的宽高 56     lp.width = WindowManager.LayoutParams.WRAP_CONTENT; 57     lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 58  59     lp.format = PixelFormat.TRANSPARENT; 60     windowManager.addView(imageView,lp); 61  62     //设置悬浮窗监听事件 63     imageView.setOnTouchListener(new View.OnTouchListener() { 64       private float lastX; //上一次位置的X.Y坐标 65       private float lastY; 66       private float nowX; //当前移动位置的X.Y坐标 67       private float nowY; 68       private float tranX; //悬浮窗移动位置的相对值 69       private float tranY; 70  71       @Override 72       public boolean onTouch(View v, MotionEvent event) { 73         boolean ret = false; 74         switch (event.getAction()){ 75           case MotionEvent.ACTION_DOWN: 76             // 获取按下时的X,Y坐标 77             lastX = event.getRawX(); 78             lastY = event.getRawY(); 79             ret = true; 80             break; 81           case MotionEvent.ACTION_MOVE: 82             // 获取移动时的X,Y坐标 83             nowX = event.getRawX(); 84             nowY = event.getRawY(); 85             // 计算XY坐标偏移量 86             tranX = nowX - lastX; 87             tranY = nowY - lastY; 88             // 移动悬浮窗 89             lp.x += tranX; 90             lp.y += tranY; 91             //更新悬浮窗位置 92             windowManager.updateViewLayout(imageView,lp); 93             //记录当前坐标作为下一次计算的上一次移动的位置坐标 94             lastX = nowX; 95             lastY = nowY; 96             break; 97           case MotionEvent.ACTION_UP: 98             break; 99         }100         return ret;101       }102     });103   }104 105 }

FloatWindowActivity.java