Android悬浮窗进阶版-中篇(代码篇)

我们上一篇已经说明了我们需要改进的三个需求,分别是:

  1. 拖拽悬浮窗,需要判断拖拽距离后才会产生拖拽效果。
  2. 根据悬浮窗在手机屏幕的位置,自动判断靠边。
  3. 悬浮窗首次绘制时的屏幕位置能随意更改。

那么,我们这一篇就来说说这三个需求的代码实现。


1.判断拖拽距离 和 自动判断靠边
FloatWindowParams 为我自己写的一个悬浮窗参数类,并不会影响到你对本代码块的逻辑及算法的理解。
顺便说下,访问修饰符为protected 都是我自写悬浮窗参数类里的参数,在这里当成成员变量一块贴了出来

    /**
     * 系统悬浮窗的参数类
     */
    protected WindowManager.LayoutParams mParams;
    /**
     * 悬浮窗布局
     */
    protected View mFloatWindowLayout;
     /**
     * 悬浮窗宽度
     */
    protected float mFloatWidth;
    /**
     * 悬浮窗高度
     */
    protected float mFloatHeight;
    /**
     * 悬浮窗靠边的参数
     */
    protected boolean mKeepSideFlag;
    /**
     * 悬浮窗参数类
     */
    private FloatWindowParams mParams;
    /**
     * ACTION_DOWN时的X坐标
     */
    private float mOldStopX;
    /**
     * ACTION_DOWN时的Y坐标
     */
    private float mOldStopY;
    /**
     * 绘制停止移动时的悬浮窗开关
     * 当判断为拖拽,允许触发ACTION_UP事件
     * 当判断未拖拽,关闭触发ACTION_UP事件
     */
    private boolean mDrawStopFloatWindowFlag;
    /**
     * 手机屏幕宽像素
     */
    private float mScreenW;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                mOldStopX = event.getRawX();
                mOldStopY = event.getRawY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                //根据移动逻辑,绘制悬浮窗口方法
                judgeMoveFloatWindow(event);
                break;
            }
            case MotionEvent.ACTION_UP: {
                if (mDrawStopFloatWindowFlag) {
                        judgeStopFloatWindow(event, 
                        mParams.mKeepSideFlag);
                }
                break;
            }
        }
        return false; //此处必须返回false,否则OnClickListener获取不到监听
    }


    /**
     * 判断移动悬浮窗口<br>
     * 通过四种情况,来判断是否达成移动条件<br>
     * 达成了-关闭点击事件开关,并实时绘制悬浮窗<br>
     * 未达成-打开点击事件开关<br>
     * 条件:(条件中的移动距离是实时计算的)<br>
     * X轴移动距离 > 悬浮窗的W<br>
     * Y轴移动距离 > 悬浮窗的H<br>
     * 情况:<br>
     * 第一种-X轴移动距离为正值,Y轴移动距离为正值 <br>
     * 第二种-X轴移动距离为正值,Y轴移动距离为负值 <br>
     * 第三种-X轴移动距离为负值,Y轴移动距离为正值 <br>
     * 第四种-X轴移动距离为负值,Y轴移动距离为负值 <br>
     *
     * @param event event
     */
    private void judgeMoveFloatWindow(MotionEvent event) {
        if (event.getRawX() - mOldStopX >= 0) {
            if (event.getRawY() - mOldStopY >= 0) {
                //判断拖拽的距离我这里写的是悬浮窗的X,Y轴像素
                if ((event.getRawX() - mOldStopX) > mParams.mFloatWidth ||
                        (event.getRawY() - mOldStopY) > mParams.mFloatHeight) {
                    mDrawStopFloatWindowFlag = true;
                    //绘制悬浮窗口
                    drawMoveFloatWindow(event);
                }
            } else if (event.getRawY() - mOldStopY < 0) {
                //判断拖拽的距离我这里写的是悬浮窗的X,Y轴像素
                if ((event.getRawX() - mOldStopX) > mParams.mFloatWidth ||
                        -(event.getRawY() - mOldStopY) > mParams.mFloatHeight) {
                    mDrawStopFloatWindowFlag = true;
                    //绘制悬浮窗口
                    drawMoveFloatWindow(event);
                }
            }

        } else if (event.getRawX() - mOldStopX < 0) {
            if (event.getRawY() - mOldStopY >= 0) {
                //判断拖拽的距离我这里写的是悬浮窗的X,Y轴像素
                if (-(event.getRawX() - mOldStopX) > mParams.mFloatWidth ||
                        (event.getRawY() - mOldStopY) > mParams.mFloatHeight) {
                    mDrawStopFloatWindowFlag = true;
                    //绘制悬浮窗口
                    drawMoveFloatWindow(event);
                }
            } else if (event.getRawY() - mOldStopY < 0) {
                //判断拖拽的距离我这里写的是悬浮窗的X,Y轴像素
                if (-(event.getRawX() - mOldStopX) > mParams.mFloatWidth ||
                        -(event.getRawY() - mOldStopY) > mParams.mFloatHeight) {
                    mDrawStopFloatWindowFlag = true;
                    //绘制悬浮窗口
                    drawMoveFloatWindow(event);
                }
            }
        }
    }

    /**
     * 判断悬浮窗停止拖拽后是否靠边<br>
     * 如果靠边,判断靠左还是靠右<br>
     *
     * @param event        event
     * @param keepSideFlag 靠边开关
     */
    private void judgeStopFloatWindow(MotionEvent event, boolean keepSideFlag) {
        if (keepSideFlag) {
            if (event.getRawX() < mScreenW / 2) {//判断悬浮窗在屏幕左半边
                drawStopFloatWindow(event, 1);
            } else if (event.getRawX() >= mScreenW / 2) {//判断悬浮窗在屏幕右半边
                drawStopFloatWindow(event, 2);
            }
        } else {//不靠边
            drawStopFloatWindow(event, 3);
        }
    }

    /**
     * 绘制移动悬浮窗口
     *
     * @param event event
     */
    private void drawMoveFloatWindow(MotionEvent event) {
        //绘制悬浮窗时,X,Y轴坐标各减去悬浮窗布局的X,Y轴的一半
        mParams.mParams.x = (int) (event.getRawX() - mParams.mFloatWidth / 2);
        mParams.mParams.y = (int) (event.getRawY() - mParams.mFloatHeight / 2);
        mWindowManager.updateViewLayout(mParams.mFloatWindowLayout, mParams.mParams);
    }

    /**
     * 绘制停止拖拽后的悬浮窗<br>
     * 根据是否靠边或靠左靠右来绘制<br>
     *
     * @param event event
     * @param site  1-LEFT,2-RIGHT,3-不靠边
     */
    private void drawStopFloatWindow(MotionEvent event, int site) {
        switch (site) {
            case 1: {
                mParams.mParams.x = 0;//靠左
                mParams.mParams.y = (int) (event.getRawY() - mParams.mFloatHeight / 2);
                mWindowManager.updateViewLayout(mParams.mFloatWindowLayout, mParams.mParams);
                mDrawStopFloatWindowFlag = false;
                break;
            }
            case 2: {
                mParams.mParams.x = (int) (mScreenW);//靠右
                mParams.mParams.y = (int) (event.getRawY() - mParams.mFloatHeight / 2);
                mWindowManager.updateViewLayout(mParams.mFloatWindowLayout, mParams.mParams);
                mDrawStopFloatWindowFlag = false;
                break;
            }
            case 3: {
                mParams.mParams.x = (int) (event.getRawX() - mParams.mFloatWidth / 2);
                mParams.mParams.y = (int) (event.getRawY() - mParams.mFloatHeight / 2);
                mWindowManager.updateViewLayout(mParams.mFloatWindowLayout, mParams.mParams);
                mDrawStopFloatWindowFlag = false;
                break;
            }
        }
    }

2.悬浮窗首次绘制的位置
以下代码块,在位置的概念上加入了除数。
让位置的概念从之前的9种一下子变得更自由更随意
说点题外话,之前没有加除数限制的时候
好像当除数大于10000还是大于100000的时候
手机会死机,真的死机哦,只能拔电池或者长按电源键

    public final static int GRAVITY_TOP = 1000000;
    public final static int GRAVITY_BOTTOM = 1000001;
    public final static int GRAVITY_LEFT = 1000002;
    public final static int GRAVITY_RIGHT = 1000003;
    public final static int GRAVITY_CENTER = 1000004;

    /**
     * 悬浮窗绘制的位置<br>
     * 可以是位置,也可以是X,Y轴坐标的除数<br>
     * 当为除数时,将会取商的一半来绘制<br>
     * 也就是说:被除数为Y轴,除数为1,商为Y轴,那么绘制点将在Y轴中间<br>
     * 如果位置逻辑错误,或者除数小于1,或者除数大于5,悬浮窗最终都将绘制在左上<br>
     * 例如:FloatWindowParams.GRAVITY_LEFT 或 正整数(1-5)
     *
     * @param x X轴位置或者除数(1-5)
     * @param y Y轴位置或者除数(1-5)
     */
    private void drawFloatWindowGravity(int x, int y) {
        switch (x) {
            case FloatWindowParams.GRAVITY_LEFT: {
                switch (y) {
                    case FloatWindowParams.GRAVITY_TOP: {//左上
                        mParams.x = 0;
                        mParams.y = 0;
                        break;
                    }
                    case FloatWindowParams.GRAVITY_BOTTOM: {//左下
                        mParams.x = 0;
                        mParams.y = (int) mScreenH;
                        break;
                    }
                    case FloatWindowParams.GRAVITY_CENTER: {//左中
                        mParams.x = 0;
                        mParams.y = (int) (mScreenH / 2 - mFloatHeight / 2);
                        break;
                    }
                    default: {//X轴为静态常量-左,Y轴为除数
                        if (0 < y && y < 6) {
                            mParams.x = 0;
                            mParams.y = (int) (mScreenH / y / 2 - mFloatHeight / 2);
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                }
                break;
            }
            case FloatWindowParams.GRAVITY_RIGHT: {
                switch (y) {
                    case FloatWindowParams.GRAVITY_TOP: {//右上
                        mParams.x = (int) mScreenW;
                        mParams.y = 0;
                        break;
                    }
                    case FloatWindowParams.GRAVITY_BOTTOM: {//右下
                        mParams.x = (int) mScreenW;
                        mParams.y = (int) mScreenH;
                        break;
                    }
                    case FloatWindowParams.GRAVITY_CENTER: {//右中
                        mParams.x = (int) mScreenW;
                        mParams.y = (int) (mScreenH / 2 - mFloatHeight / 2);
                        break;
                    }
                    default: {//X轴为静态常量-右,Y轴为除数
                        if (0 < y && y < 6) {
                            mParams.x = (int) mScreenW;
                            mParams.y = (int) (mScreenH / y / 2 - mFloatHeight / 2);
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                }
                break;
            }
            case FloatWindowParams.GRAVITY_CENTER: {
                switch (y) {
                    case FloatWindowParams.GRAVITY_TOP: {//中上
                        mParams.x = (int) (mScreenW / 2 - mFloatWidth / 2);
                        mParams.y = 0;
                        break;
                    }
                    case FloatWindowParams.GRAVITY_BOTTOM: {//中下
                        mParams.x = (int) (mScreenW / 2 - mFloatWidth / 2);
                        mParams.y = (int) mScreenH;
                        break;
                    }
                    case FloatWindowParams.GRAVITY_CENTER: {//中中
                        mParams.x = (int) (mScreenW / 2 - mFloatWidth / 2);
                        mParams.y = (int) (mScreenH / 2 - mFloatHeight / 2);
                        break;
                    }
                    default: {//X轴为静态常量-中,Y轴为除数
                        if (0 < y && y < 6) {
                            mParams.x = (int) (mScreenW / 2 - mFloatWidth / 2);
                            mParams.y = (int) (mScreenH / y / 2 - mFloatHeight / 2);
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                }
                break;
            }
            default: {//X轴为除数
                switch (y) {
                    case FloatWindowParams.GRAVITY_TOP: {//X轴为除数,Y轴为静态常量-上
                        if (0 < x && x < 6) {
                            mParams.x = (int) (mScreenW / x / 2 - mFloatWidth / 2);
                            mParams.y = 0;
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                    case FloatWindowParams.GRAVITY_BOTTOM: {//X轴为除数,Y轴为静态常量-下
                        if (0 < x && x < 6) {
                            mParams.x = (int) (mScreenW / x / 2 - mFloatWidth / 2);
                            mParams.y = (int) mScreenH;
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                    case FloatWindowParams.GRAVITY_CENTER: {//X轴为除数,Y轴为静态常量-中
                        if (0 < x && x < 6) {
                            mParams.x = (int) (mScreenW / x / 2 - mFloatWidth / 2);
                            mParams.y = (int) (mScreenH / 2 - mFloatHeight / 2);
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                    default: {//X轴为除数,Y轴为除数
                        if (0 < x && x < 6 && 0 < y && y < 6) {
                            mParams.x = (int) (mScreenW / x / 2 - mFloatWidth / 2);
                            mParams.y = (int) (mScreenH / y / 2 - mFloatHeight / 2);
                        } else {
                            mParams.x = 0;
                            mParams.y = 0;
                        }
                        break;
                    }
                }
                break;
            }
        }
    }

代码篇这里也分享完了
可能有朋友觉得这些代码没有办法直接粘贴复制,不够实用
那我只能说,朋友们将本篇内容理解错误了
做为代码篇,我的用意只是想通过代码的形式,让各位朋友理解代码本身的逻辑及算法
至于实用,我想说,朋友们不要着急。
又实用又方便又简单,即可以直接粘贴复制又可以随意更改的悬浮窗工具类
将会在Android悬浮窗进阶版-下篇(福利篇)放出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值