当前位置:   article > 正文

Android 悬浮窗

android 悬浮窗

悬浮窗是可以在不同软件最上面,默认的效果,不需要过多设置,通常放在服务里面,因为需要长时间存在

思路

写一个悬浮窗大概是以下几个步骤
1、写一个服务,因为悬浮窗长期存在,不依赖于界面,所有最好写在服务里面。
2、在服务需要获取到WindowManager这个类,用来加载一个悬浮窗的布局和一些列点击事件。
3、启动服务,悬浮窗就可以启动。

难点

1、悬浮窗的穿透点击
当悬浮窗悬浮的时候,理想状态,应该是悬浮窗里面的按钮和悬浮窗底层点击触摸事件不冲突。
2、需要注意,悬浮窗的可能会出现黑色背景,需要加params.format = PixelFormat.RGBA_8888;

代码逻辑

1、写一个服务

public class BackService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2、在服务里面写一个一个方法,去创建一个一个悬浮窗的样式

    /**
     * 初始化一个悬浮窗
     */
    private void initWindow() {
        // 获取WindowManager
        mSystemService = (WindowManager) getSystemService(WINDOW_SERVICE);
        // 创建布局参数
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        //这里需要进行不同的设置
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            params.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
        //设置透明度
        params.alpha = 1.0f;
        //设置内部视图对齐方式
        params.gravity = Gravity.RIGHT | Gravity.BOTTOM;
        //窗口的右上角角坐标
        params.x = 20;
        params.y = 20;
        //是指定窗口的像素格式为 RGBA_8888。
        //使用 RGBA_8888 像素格式的窗口可以在保持高质量图像的同时实现透明度效果。
        params.format = PixelFormat.RGBA_8888;
        //设置窗口的宽高,这里为自动
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        //这段非常重要,是后续是否穿透点击的关键
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  //表示悬浮窗口不需要获取焦点,这样用户点击悬浮窗口以外的区域,就不需要关闭悬浮窗口。
                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;//表示悬浮窗口不会阻塞事件传递,即用户点击悬浮窗口以外的区域时,事件会传递给后面的窗口处理。
        //这里的引入布局文件的方式,也可以动态添加控件
        mView = View.inflate(getApplicationContext(), R.layout.item_back, null);
        Button btnBack = mView.findViewById(R.id.btn_back);
        mSystemService.addView(mView,params);
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

ps:此处要注意一下,当服务销毁的时候,需要记得,把布局的view给removeView

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mSystemService != null && mView != null){
            mSystemService.removeView(mView);
        }
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3、启动服务,悬浮窗就可以启动

startService(new Intent(context, BackService.class));

  • 1
  • 2

注意

1、需要注意在悬浮窗的点击中,需要效果是悬浮窗里面的按钮和悬浮窗底层点击触摸事件不冲突,关键代码是这儿

params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  //表示悬浮窗口不需要获取焦点,这样用户点击悬浮窗口以外的区域,就不需要关闭悬浮窗口。
                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;//表示悬浮窗口不会阻塞事件传递,即用户点击悬浮窗口以外的区域时,事件会传递给后面的窗口处理。

  • 1
  • 2
  • 3

2、悬浮窗如果出现黑色背景,必须加这儿

//是指定窗口的像素格式为 RGBA_8888。
//使用 RGBA_8888 像素格式的窗口可以在保持高质量图像的同时实现透明度效果。
params.format = PixelFormat.RGBA_8888;

  • 1
  • 2
  • 3
  • 4

3、如果要隐藏当前的avtivity,只有悬浮窗,可以通过moveTaskToBack(true);设置
当activity的启动模式是singleInstance的时候,在当前的activity直接调用moveTaskToBack(true),即可将activity 退到后台
参数说明:
参数为false——代表只有当前activity是task根,指应用启动的第一个activity时,才有效;
参数为true——则忽略这个限制,任何activity都可以有效。
设置avtivity启动模式在AndroidManifest里面

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:exported="true"
    android:launchMode="singleInstance">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

整体代码

服务里面,启动服务很简单

public class BackService extends Service {

    private View mView;
    private WindowManager mSystemService;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initWindow();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mSystemService != null && mView != null){
            mSystemService.removeView(mView);
        }
    }

    /**
     * 初始化一个悬浮窗
     */
    private void initWindow() {
        // 获取WindowManager
        mSystemService = (WindowManager) getSystemService(WINDOW_SERVICE);
        // 创建布局参数
        WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            params.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
        //设置透明度
        params.alpha = 1.0f;
        //设置内部视图对齐方式
        params.gravity = Gravity.RIGHT | Gravity.BOTTOM;
        //窗口的左上角坐标
        params.x = 20;
        params.y = 20;
        //是指定窗口的像素格式为 RGBA_8888。
        //使用 RGBA_8888 像素格式的窗口可以在保持高质量图像的同时实现透明度效果。
        params.format = PixelFormat.RGBA_8888;
        //设置窗口的宽高,这里为自动
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  //表示悬浮窗口不需要获取焦点,这样用户点击悬浮窗口以外的区域,就不需要关闭悬浮窗口。
                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;//表示悬浮窗口不会阻塞事件传递,即用户点击悬浮窗口以外的区域时,事件会传递给后面的窗口处理。
        mView = View.inflate(getApplicationContext(), R.layout.item_back, null);
        Button btnBack = mView.findViewById(R.id.btn_back);
        btnBack.setOnClickListener(view1 -> {
            ToastUtils.showShort("点击了");//此处是点击逻辑,可以自己完成
        });
        mSystemService.addView(mView,params);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

效果图

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/500755
推荐阅读
相关标签
  

闽ICP备14008679号