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

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

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

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

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

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

注意

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

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

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

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

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>

整体代码

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

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

效果图

最后

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

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

全套视频资料:

一、面试合集

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

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

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值