WindowManager——悬浮在所有app之上

目标

类似于某鱼、某猫直播平台的悬浮窗功能,能够在APP切换到后台后进行播放直播。
是不是很酷炫?
WindowManager很简单就能实现将一个View悬浮在系统上。

WindowManager

WindowManaget是Android最重要的服务之一,WindowManager是全局唯一的。
其将View和屏幕联系起来,用户的交互操作也传递给它,作为桥梁完成逻辑处理与视图的通讯。
常用的也就三个方法:addView(),removeView(),updateViewLayout()。用于添加、删除、更新视图。

1 . 简单的获取WindowManager对象:

// 获取WindowManager对象
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

2 .获取一个View视图

// 获取一个View 
mView = LayoutInflater.from(mContext).inflate(R.layout.window_test, null);

3 .创建WindowManager.LayoutParams

      WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
      // 设置为始终
      layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
      // 设置弹出的Window不持有焦点
      layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
      // 大小
      layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
      layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
      // 位置
      layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
      // 设置背景透明
      layoutParams.format = PixelFormat.TRANSLUCENT;
      return layoutParams;

4 .显示、隐藏View

// 显示
mWindowManager.addView(mView, mParams);
// 隐藏
mWindowManager.removeView(mView);

WindowManager.LayoutParams参数细节,具体看源码 -_-!!!也可以看这儿

效果实现

一个简单的效果:悬浮在整个系统之上,同时能够随着手指移动Window。

/**
 * 测试Window
 *
 * @author fengzhen
 * @version v1.0, 2017/7/14
 */
public class TestWindow {

    private Context mContext;
    private final WindowManager mWindowManager;
    private View mView;

    private boolean isShowing = false;
    private WindowManager.LayoutParams mParams;

    public TestWindow(Context context) {
        this.mContext = context;
        // 根据 WINDOW_SERVICE 获取到 WindowManager
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    }

    /**
     * 创建布局参数
     *
     * @author fengzhen
     * @version v1.0, 2017/7/14 17:05
     */
    private WindowManager.LayoutParams createWindowParams() {
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        // 设置为始终顶层
        layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        // 设置弹出的Window不持有焦点
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        // 大小
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        // 位置
        layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
        // 设置背景透明
        layoutParams.format = PixelFormat.TRANSLUCENT;
        return layoutParams;
    }

    /**
     * 展示弹框
     *
     * @author fengzhen
     * @version v1.0, 2017/7/14 17:14
     */
    public void show() {

        if (isShowing) return;

        if (mView == null) {
            mView = LayoutInflater.from(mContext).inflate(R.layout.window_test, null);
            //如果需要拖动取消下面注释
            mView.findViewById(R.id.btn_hide).setOnTouchListener(new View.OnTouchListener() {

                int startX;
                int startY;
                int startDownX;
                int startDownY;

                @Override
                public boolean onTouch(View v, MotionEvent event) {

                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN: // 按下

                            // 手指按下时的坐标位置
                            startX = (int) event.getRawX();
                            startY = (int) event.getRawY();
                            startDownX = (int) event.getRawX();
                            startDownY = (int) event.getRawY();
                            break;
                        case MotionEvent.ACTION_MOVE: // 移动

                            // 移动后的坐标位置
                            int newX = (int) event.getRawX();
                            int newY = (int) event.getRawY();

                            // 偏移量
                            int dx = newX - startX;
                            int dy = newY - startY;

                            // 给偏移量设置边距
                            // 小于x轴
                            if (mParams.x < 0) {
                                mParams.x = 0;
                            }
                            // 小于y轴
                            if (mParams.y < 0) {
                                mParams.y = 0;
                            }

                            // 超出x轴
                            if (mParams.x > mWindowManager.getDefaultDisplay().getWidth() - mView.getWidth()) {
                                mParams.x = mWindowManager.getDefaultDisplay().getWidth() - mView.getWidth();
                            }
                            // 超出y轴
                            if (mParams.y > mWindowManager.getDefaultDisplay().getHeight() - mView.getHeight()) {
                                mParams.y = mWindowManager.getDefaultDisplay().getHeight() - mView.getHeight();
                            }

                            // 更新窗体的坐标
                            mParams.x += dx;
                            mParams.y += dy;
                            mWindowManager.updateViewLayout(mView, mParams);

                            // 重新赋值起始坐标
                            startX = (int) event.getRawX();
                            startY = (int) event.getRawY();
                            break;
                        case MotionEvent.ACTION_UP: // 抬起
                            int upX = (int) event.getRawX();
                            int upY = (int) event.getRawY();
                            // 更新窗体的坐标
                            if (upX < mWindowManager.getDefaultDisplay().getWidth() / 2) {
                                for (int i = 0; i < mWindowManager.getDefaultDisplay().getWidth() / 2; i++) {
                                    mParams.x = upX - i;
                                    mWindowManager.updateViewLayout(mView, mParams);
                                }
                            } else {
                                for (int i = 0; i < mWindowManager.getDefaultDisplay().getWidth() / 2; i++) {
                                    mParams.x = upX + i;
                                    mWindowManager.updateViewLayout(mView, mParams);
                                }
                                mParams.x = mWindowManager.getDefaultDisplay().getHeight();
                            }
                            break;
                        default:
                            break;
                    }

                    return true;  // 当有父控件有点击事件时,这里要返回false,不然父控件就拿不到点击事件了
                }
            });

            mParams = createWindowParams();
        }

        mWindowManager.addView(mView, mParams);
        isShowing = true;
    }

    /**
     * 隐藏
     *
     * @author fengzhen
     * @version v1.0, 2017/7/14 17:14
     */
    public void hide() {
        if (!isShowing) return;

        if (mWindowManager != null && mView != null) {
            mWindowManager.removeView(mView);
            isShowing = false;
        }
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值