Android悬浮窗实现 使用WindowManager

Android  2020年8月14日 pm12:31发布4年前 (2020)更新 城堡大人
93 0 0

WindowManager介绍

通过Context.getSystemService(Context.WINDOW_SERVICE)可以获得 WindowManager对象。

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

调用的是WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口。

LayoutParams常用参数:

  1. type值

用于确定悬浮窗的类型,一般设为2002,表示在所有应用程序之上,但在状态栏之下。

  1. flags值

用于确定悬浮窗的行为,比如说不可聚焦,非模态对话框等等,属性非常多,大家可以查看文档。

  1. gravity值

用于确定悬浮窗的对齐方式,一般设为左上角对齐,这样当拖动悬浮窗的时候方便计算坐标。

  1. x值

用于确定悬浮窗的位置,如果要横向移动悬浮窗,就需要改变这个值。

  1. y值

用于确定悬浮窗的位置,如果要纵向移动悬浮窗,就需要改变这个值。

  1. width值

用于指定悬浮窗的宽度。

  1. height值

用于指定悬浮窗的高度。

WindowManager实现悬浮窗

声明权限

AndroidManifest.xml中添加如下代码

<!-- 显示顶层浮窗 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

注意:

Android 6.0以上【非系统应用】,需要在[系统设置]找到[应用和通知]已经的[我们的应用],然后点击进入[高级],打开[在应用上方显示]开关

demo代码片段

MainActivity.java

public class WindowActivity extends Activity {

    private String TAG = getClass().getSimpleName();

    private WindowManager windowManager;
    private WindowManager.LayoutParams mLayoutParams = null;
    private View seekbarLayoutView = null;
    private boolean hasWindow = false;
    private LayoutInflater inflater = null;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate : ");
        setContentView(R.layout.activity_main);
        initUI();
        initWindow();
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }

    private void initUI() {

        findViewById(R.id.main_bt_one).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick  show(): ");
                show();
            }
        });

        return;
    }

    private void initWindow() {
        inflater = LayoutInflater.from(getApplicationContext());
        windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        mLayoutParams = new WindowManager.LayoutParams();

        //设置透明度
        mLayoutParams.alpha = 1.0f;

        //Android 6.0后,非系统应用,需要在[系统设置]找到[应用和通知]已经的[我们的应用],然后点击进入[高级],打开[在应用上方显示]开关
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0 以上
            mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }

        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;

        mLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        mLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
        seekbarLayoutView = inflater.inflate(R.layout.topbar_layout, null);
        seekbarLayoutView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick hide(): ");
                hide();
            }
        });
        return;
    }

    private void show() {
        Log.d(TAG, "show hasWindow : " + hasWindow);
        if (null != windowManager && (null != seekbarLayoutView) &&
                (seekbarLayoutView.getParent() == null) && !hasWindow) {
            hasWindow = true;
            windowManager.addView(seekbarLayoutView, mLayoutParams);
        }
        return;
    }

    private void hide() {
        Log.d(TAG, "hide hasWindow : " + hasWindow);
        if (null != windowManager && (null != seekbarLayoutView) && hasWindow) {
            windowManager.removeView(seekbarLayoutView);
            hasWindow = false;
        }
        return;
    }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="@android:color/darker_gray"
    android:orientation="vertical">


    <Button
        android:id="@+id/main_bt_one"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:gravity="center"
        android:text="one" />

</LinearLayout>

topbar_layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_green_dark"
    android:paddingTop="160dp"
    tools:ignore="MissingDefaultResource">

</RelativeLayout>

参考文档

  1. WindowManager.LayoutParams
  2. Android 之 Window、WindowManager 与窗口管理

 历史上的今天

  1. 2023: Android消息机制源码介绍(0条评论)
  2. 2019: 梁实秋:时间即生命(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

王小波:人为什么活着

银河,你好!我在家里给你写信。你问我人为什么活着,我哪能知道啊?我又不是牧师。释迦牟尼为了解决这个问题出了家,结果得到的结论是人活着为了涅,就是死。这简直近乎开玩笑了。不过活着总得死,这一点是不错的,我有时对这一点也很不满意呢。还有人活着有时候有点闷,这也是很不愉快的。过去我想,人活着都得为别人...

Glide V4和V3 使用不同

Glide是目前 Android 上最流行的图片加载库之一。目前Glide已经更新到了4.11.0repositories { mavenCentral() google()}dependencies { implementation 'com.github.bumptech...

ADB更改系统设置中的参数

 Android4.2以后的源码android-17\com\android\commands目录下较之前的版本多了一个settings命令,查看其中的SettingsCmd.java文件,末尾有命令的帮助信息:private static void printUsage() { ...

Android P去除MediaProvider编译

前言记录一下Android P去除MediaProvider编译。记录于此,方便自己查阅。正文隐藏内容!付费阅读后才能查看!¥1 ¥3多个隐藏块只需支付一次付费阅读参考文章

丰子恺:渐

不为“渐”所迷,不为造物所欺,而收缩无限的时间并空间于方寸的心中。故佛家能纳须弥于芥子。使人生圆滑进行的微妙的要素,莫如“渐”;造物主骗人的手段,也莫如“渐”。在不知不觉之中,天真烂漫的孩子“渐渐”变成野心勃勃的青年;慷慨豪侠的青年“渐渐”变成冷酷的成人;血气旺盛的成人“渐渐”变成顽固的老头子。因为...

内存分析工具MAT简单记录

前言MAT(Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,...