Android悬浮窗进阶版-下篇(福利篇)

来源:互联网 发布:雅思高频词汇 知乎 编辑:程序博客网 时间:2024/04/29 16:40

在这一篇,我会分享自己写的悬浮窗工具类。
特点:
1.使用简单
2.已经写好移动事件(拖拽距离判断,拖拽时防点击事件)
3.自定义悬浮窗首次绘制位置(是自写的)
4.是否自动靠边(X轴两边)
不足:
自己项目中写的,必然功能是满足自己项目的。
没有办法满足全部朋友,有不同需求的可以自行更改。


工具类包含了三大类
1.FloatWindowUtil(此类模拟了WindowManager)
2.FloatWindowParams(此类模拟了WindowManager.LayoutParams)
3.FloatWindowMoveListener(实现了View.OnTouchListener,实现了悬浮窗移动事件)

FloatWindowUtil公开方法
getNewParams()
获取一个实例化过的FloatWindowParams类
addView()
模拟WindowManager的addView()
updateViewLayout()
模拟WindowManager的updateViewLayout()
removeView()
模拟WindowManager的removeView()

FloatWindowParams公开方法
setOnFloatWindowMoveListener()
为悬浮窗布局控件绑定移动事件
removeFloatWindowMoveListener()
为悬浮窗布局控件移除移动事件
setGravity()
为悬浮窗初次绘制设定位置
setKeepSide()
为悬浮窗移动后设定是否靠边(X轴两边)
setType()
模拟设置WindowManager.LayoutParams.type参数
setWH()
模拟设置WindowManager.LayoutParams.width参数
模拟设置WindowManager.LayoutParams.height参数
setFlags()
模拟设置WindowManager.LayoutParams.flags参数
setFormat()
模拟设置WindowManager.LayoutParams.format参数

FloatWindowMoveListener无公开方法,并且不需要自己实例化


FloatWindowUtil类代码

package ;import android.app.Application;import android.content.Context;import android.view.View;import android.view.WindowManager;/** * 项目名称:<br> * 类描述:悬浮窗工具类<br> * 创建人:梁伟<br> * 创建时间:2016/3/10 17:16<br> * 修改人:梁伟<br> * 修改时间:2016/3/10 17:16<br> * 修改备注:<br> */public class FloatWindowUtil {    /**     * 系统悬浮窗管理类     */    private WindowManager mWindowManager;    /**     * 实例化WindowManager     *     * @param application 全局     */    public FloatWindowUtil(Application application) {        mWindowManager = (WindowManager) application.getSystemService(Context.WINDOW_SERVICE);    }    /**     * 添加悬浮窗<br>     *     * @param params 悬浮窗参数     */    public void addView(FloatWindowParams params) {        mWindowManager.addView(params.mFloatWindowLayout, params.mParams);    }    /**     * 移除悬浮窗     *     * @param params 悬浮窗参数     */    public void removeView(FloatWindowParams params) {        mWindowManager.removeView(params.mFloatWindowLayout);    }    /**     * 更新悬浮窗     *     * @param params 悬浮窗参数     */    public void updateViewLayout(FloatWindowParams params) {        mWindowManager.updateViewLayout(params.mFloatWindowLayout, params.mParams);    }    /**     * 自动初始化系统参数类,并设值<br>     * 悬浮窗参数为:<br>     * 悬浮窗是否靠边-靠边(工具类自定义参数)<br>     * wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;<br>     * wmParams.format = PixelFormat.RGBA_8888;<br>     * wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;<br>     * wmParams.gravity = Gravity.LEFT | Gravity.TOP;<br>     * wmParams.x = 0;<br>     * wmParams.y = 0;<br>     * wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;<br>     * wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;<br>     *     * @param layout 悬浮窗布局     * @return 获得悬浮窗参数类     */    public FloatWindowParams getNewParams(View layout) {        return new FloatWindowParams(layout, mWindowManager);    }}

FloatWindowParams类代码

package ;import android.graphics.PixelFormat;import android.view.View;import android.view.ViewTreeObserver;import android.view.WindowManager;import static android.view.Gravity.START;import static android.view.Gravity.TOP;/** * 项目名称:<br> * 类描述:悬浮窗参数类<br> * 创建人:梁伟<br> * 创建时间:2016/3/15 16:47<br> * 修改人:梁伟<br> * 修改时间:2016/3/15 16:47<br> * 修改备注:<br> */public class FloatWindowParams {    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;    /**     * 系统悬浮窗管理类     */    private WindowManager mWindowManager;    /**     * 系统悬浮窗的参数类     */    protected WindowManager.LayoutParams mParams;    /**     * 悬浮窗布局     */    protected View mFloatWindowLayout;    /**     * 悬浮窗布局控件的数组     */    private View[] mViews;    /**     * 悬浮窗X轴位置     */    private int mGravityX;    /**     * 悬浮窗Y轴位置     */    private int mGravityY;    /**     * 悬浮窗宽度     */    protected float mFloatWidth;    /**     * 悬浮窗高度     */    protected float mFloatHeight;    /**     * 手机屏幕宽像素     */    private float mScreenW;    /**     * 手机屏幕高像素     */    private float mScreenH;    /**     * 悬浮窗靠边的参数     */    protected boolean mKeepSideFlag;    /**     * 计算悬浮窗布局宽高时用到的事件     */    private ViewTreeObserver.OnGlobalLayoutListener mListener;    protected FloatWindowParams(View layout, WindowManager windowManager) {        mWindowManager = windowManager;        mFloatWindowLayout = layout;        //获取手机屏幕宽高        mScreenW = windowManager.getDefaultDisplay().getWidth();        mScreenH = windowManager.getDefaultDisplay().getHeight();        //获取悬浮窗布局宽高,并刷新悬浮窗,后移除事件        mListener = new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                mFloatWidth = mFloatWindowLayout.getWidth();                mFloatHeight = mFloatWindowLayout.getHeight();                drawFloatWindowGravity(mGravityX, mGravityY);                mWindowManager.updateViewLayout(mFloatWindowLayout, mParams);                mFloatWindowLayout.getViewTreeObserver().removeOnGlobalLayoutListener(mListener);            }        };        //给悬浮窗布局添加事件        mFloatWindowLayout.getViewTreeObserver().addOnGlobalLayoutListener(mListener);        createParams();    }    /**     * 实例化悬浮窗参数<br>     * 悬浮窗参数固定为:<br>     * 悬浮窗是否靠边-靠边(工具类自定义参数)<br>     * wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;<br>     * wmParams.format = PixelFormat.RGBA_8888;<br>     * wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;<br>     * wmParams.gravity = Gravity.LEFT | Gravity.TOP;<br>     * wmParams.x = 0;<br>     * wmParams.y = 0;<br>     * wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;<br>     * wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;<br>     */    private void createParams() {        //初始化悬浮窗的参数类        mParams = new WindowManager.LayoutParams();        //设置window type        mParams.type = WindowManager.LayoutParams.TYPE_PHONE;        //效果悬浮窗为背景透明,可能因为手机或版本的原因不一定一定透明        mParams.format = PixelFormat.RGBA_8888;        //设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;        //调整悬浮窗显示的停靠位置为左侧置顶        mParams.gravity = START | TOP;        // 设置x、y初始值,相对于gravity的参数值,不设默认为0        mParams.x = 0;        mParams.y = 0;        //设置悬浮窗口长宽数据        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;        //悬浮窗靠边        mKeepSideFlag = true;    }    /**     * 对悬浮窗绑定移动事件<br>     * 绑定移动事件时,建议将所有布局中的控件都绑定<br>     * 因为ViewGroup子类无法通过setOnTouchListener绑定<br>     * 必须自写类继承ViewGroup或其子类,然后重写特定方法才可以实现<br>     * 所以建议这里用取巧的办法,将所有控件都绑定<br>     *     * @param views 悬浮窗布局控件     */    public void setOnFloatWindowMoveListener(View... views) {        mViews = views;        for (View view : mViews) {            view.setOnTouchListener(new FloatWindowMoveListener(this, mWindowManager));        }    }    /**     * 移除悬浮窗移动事件     */    public void removeFloatWindowMoveListener() {        for (View view : mViews) {            view.setOnTouchListener(null);        }    }    /**     * 系统Params-width-height<br>     * 例如:WindowManager.LayoutParams.WRAP_CONTENT<br>     *     * @param width  宽数据     * @param height 高数据     */    public void setWH(int width, int height) {        mParams.width = width;        mParams.height = height;    }    /**     * 系统Params-flags<br>     * 例如:WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE<br>     *     * @param flags 。     */    public void setFlags(int flags) {        mParams.flags = flags;    }    /**     * 系统Params-format<br>     * 例如:PixelFormat.RGBA_8888<br>     *     * @param format。     */    public void setFormat(int format) {        mParams.format = format;    }    /**     * 系统Params-type<br>     * 例如:WindowManager.LayoutParams.TYPE_PHONE<br>     *     * @param type 。     */    public void setType(int type) {        mParams.type = type;    }    /**     * 悬浮窗首次绘制的位置<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)     */    public void setGravity(int x, int y) {        mGravityX = x;        mGravityY = y;    }    /**     * 设置悬浮窗是否靠边     *     * @param flag 开关     */    public void setKeepSide(boolean flag) {        mKeepSideFlag = flag;    }    /**     * 悬浮窗绘制的位置<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: {                        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: {                        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: {                        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: {                switch (y) {                    case FloatWindowParams.GRAVITY_TOP: {                        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: {                        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: {                        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: {                        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;            }        }    }}

FloatWindowMoveListener类代码

package ;import android.view.MotionEvent;import android.view.View;import android.view.WindowManager;/** * 项目名称:<br> * 类描述:悬浮窗移动事件类<br> * 已经实现悬浮窗移动<br> * 创建人:梁伟<br> * 创建时间:2016/3/15 17:11<br> * 修改人:梁伟<br> * 修改时间:2016/3/15 17:11<br> * 修改备注:<br> */public class FloatWindowMoveListener implements View.OnTouchListener {    /**     * 悬浮窗参数类     */    private FloatWindowParams mParams;    /**     * ACTION_DOWN时的X坐标     */    private float mOldStopX;    /**     * ACTION_DOWN时的Y坐标     */    private float mOldStopY;    /**     * 绘制停止移动时的悬浮窗开关     */    private boolean mDrawStopFloatWindowFlag;    /**     * 是否关闭点击事件     */    private boolean mIsCloseClick;    /**     * 手机屏幕宽像素     */    private float mScreenW;    /**     * 手机屏幕高像素     */    private float mScreenH;    /**     * 浮动窗口对象     */    private WindowManager mWindowManager;    protected FloatWindowMoveListener(FloatWindowParams params, WindowManager windowManager) {        mWindowManager = windowManager;        mParams = params;        mScreenW = mWindowManager.getDefaultDisplay().getWidth();        mScreenH = mWindowManager.getDefaultDisplay().getHeight();    }    @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);                }                if (mIsCloseClick) {                    return true;                } else {                    return false;                }            }        }        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) {                if ((event.getRawX() - mOldStopX) > mParams.mFloatWidth / 2 ||                        (event.getRawY() - mOldStopY) > mParams.mFloatHeight / 2) {                    mDrawStopFloatWindowFlag = true;                    //绘制悬浮窗口                    drawMoveFloatWindow(event);                    mIsCloseClick = true;                } else {                    mIsCloseClick = false;                }            } else if (event.getRawY() - mOldStopY < 0) {                if ((event.getRawX() - mOldStopX) > mParams.mFloatWidth / 2 ||                        -(event.getRawY() - mOldStopY) > mParams.mFloatHeight / 2) {                    mDrawStopFloatWindowFlag = true;                    //绘制悬浮窗口                    drawMoveFloatWindow(event);                    mIsCloseClick = true;                } else {                    mIsCloseClick = false;                }            }        } else if (event.getRawX() - mOldStopX < 0) {            if (event.getRawY() - mOldStopY >= 0) {                if (-(event.getRawX() - mOldStopX) > mParams.mFloatWidth / 2 ||                        (event.getRawY() - mOldStopY) > mParams.mFloatHeight / 2) {                    mDrawStopFloatWindowFlag = true;                    //绘制悬浮窗口                    drawMoveFloatWindow(event);                    mIsCloseClick = true;                } else {                    mIsCloseClick = false;                }            } else if (event.getRawY() - mOldStopY < 0) {                if (-(event.getRawX() - mOldStopX) > mParams.mFloatWidth / 2 ||                        -(event.getRawY() - mOldStopY) > mParams.mFloatHeight / 2) {                    mDrawStopFloatWindowFlag = true;                    //绘制悬浮窗口                    drawMoveFloatWindow(event);                    mIsCloseClick = true;                } else {                    mIsCloseClick = false;                }            }        }    }    /**     * 判断悬浮窗停止拖拽后是否靠边<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) {        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;            }        }    }}

使用方法
1.实例化FloatWindowUtil
2.FloatWindowUtil.getNewParams()获取实例化过的FloatWindowParams
3.通过FloatWindowParams的公开方法设置悬浮窗参数
4.通过FloatWindowUtil的公开方法,添加,更新,移除悬浮窗

0 0
原创粉丝点击