简易的可拖动的桌面悬浮窗效果Demo

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

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

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

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

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

1、配置清单文件AndroidManifest.xml 中 添加系统悬浮窗的权限

    <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 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

achartengine之折线图

问题在文章的最后,大致说来就是折线图,如果点的个数大于3个的时候,不是所有的点都显示对应的值的,这是为什么呢,本来以为是小问题,但两天了还没找到原因) 将前...

27810
来自专栏指尖下的Android

Android 仿UC浏览器详情页评论弹框效果

额,突然发现UC被我卸载了,这个是QQ浏览器的效果,不过都一样,如果当前页面不是全屏的话,把根布局设为相对布局,然后设置评论布局为处于底部,这样在点击评论时弹开...

1333
来自专栏james大数据架构

Android LayoutInflater详解

在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/l...

2289
来自专栏Android干货

Android项目实战(三):实现第一次进入软件的引导页

3215
来自专栏xingoo, 一个梦想做发明家的程序员

【插件开发】—— 5 SWT控件以及布局使用

前文回顾: 1 插件学习篇 2 简单的建立插件工程以及模型文件分析 3 利用扩展点,开发透视图 4 SWT编程须知   经过前几篇的介绍,多少对S...

2079
来自专栏Android知识点总结

Android材料设计之FloatingActionButton+Snackbar+SheetX3

973
来自专栏指尖下的Android

Matisse预览图片黑屏,Glide内存溢出

项目中要到图片、视频选择的功能,然后google了一下,找到Matisse,知乎的图片选择框架,用的人还挺多的,果断依赖gradle,然后开始我的踩坑之旅。 ...

2271
来自专栏向治洪

android抓屏

现在无论是应用,还是游戏中,都经常会有分享的功能。分享,不仅要分享文字,也要分享应用或者游戏的屏幕截图,这样才能做到图文并茂,吸引到更多的用户。 想要做图片的分...

2157
来自专栏向治洪

BottomSheet底部动作条使用

底部动作条 底部动作条(Bottom Sheets)是一个从屏幕底部边缘向上滑出的一个面板,使用这种方式向用户呈现一组功能。底部动作条呈现了简单、清晰、无需额...

2558
来自专栏GIS讲堂

用百度地图API打造方便自己使用的手机地图

有钱人咱就不说了,因为偶是个穷银……因为穷,所以去年买的Huawei C8650+到现在还在上岗,对于没有钱买好的配置的手机的童鞋来说,类似于百度,谷歌,高德等...

5514

扫码关注云+社区

领取腾讯云代金券