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的公开方法,添加,更新,移除悬浮窗
- Android悬浮窗进阶版-下篇(福利篇)
- Android悬浮窗进阶版-上篇(思路篇)
- Android悬浮窗进阶版-中篇(代码篇)
- Android进阶-悬浮窗
- Android悬浮窗进阶版,QQ手机管家小火箭效果实现
- Android进阶2之PopupWindow弹窗(有点悬浮窗的感觉)
- Android进阶2之PopupWindow弹窗(有点悬浮窗的感觉)
- 仿360悬浮窗——进阶篇
- 【Android进阶】天天动听 悬浮歌词
- Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果&Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶---QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- Android桌面悬浮窗进阶,QQ手机管家小火箭效果实现
- 欢迎使用CSDN-markdown编辑器
- 相邻最大差值
- solr-结合tomcat安装
- 最接近的数
- 录音,录视频功能的实现过程
- Android悬浮窗进阶版-下篇(福利篇)
- Unable to add window崩溃分析
- java---String演示
- 最长子序列问题——动态规划算法初解
- Lily:基于Solr、HBase、Zookeeper云计算上的内容仓库
- Activity与Fragment的生命周期
- 网络爬虫相关软件以及论文检索与推荐网站调研
- 隐藏手机的软键盘
- android.intent.action.SCREEN_ON与android.intent.action.SCREEN_OFF