实现可拖拽移动的悬浮按钮
来源:互联网 发布:淘宝邮箱注册页面 编辑:程序博客网 时间:2024/05/16 08:23
前言:
最近想要实现一个可拖拽移动的FAB按钮,这里记录一下个人的思路与经验。
如何监听FAB按钮的移动?
我们可以实现View.OnTouchListener接口,在onTouch( )方法中获取FAB按钮移动时的位置参数。或者可以选择复写View自身的onTouchEvent( )方法,实现方式大同小异。
移动范围超出屏幕怎么办?
通过逻辑判断限制FAB移动的范围。
如何区分FAB按钮的”拖拽移动” 和 “点击事件?”
如果同时监听FAB按钮onTouch和onClick事件 , 在onTouch事件中执行拖动操作,在onClick中实现事件逻辑,但是onTouch和OnClick会有冲突。
onTouch事件的返回值是boolean类型的,如果返回true ,那么事件就会被拦截,onclick方法不会被调用;返回false,事件不被消费和拦截,onClick方法会同时被调用。
策略一:要想把OnTouch和onClick事件完全的区分,这里的想法就是在 OnTouch中的MotionEvent.ACTION_DOWN 时,记录下点(X1,Y1),在 MotionEvent.ACTION_UP 时,记录下点(X2,Y2),然后比对 俩点之间的距离,如果小于一个较小数值(比如5),就认为是Click事件,onTouch中返回false,如果距离较大,可以当作onTouch事件去处理,返回true.
策略二:
如何实现FAB按钮的移动?
通过监听OnTouch( )方法或者覆写onTouchEvent( )方法,我们已经可以获取到拖拽FAB按钮时的实时位置了,接下来我们如何表现这种位置移动的“变化“?
通过view.layout()方式。任何布局上的空间都可以支持这种方式移动,上下左右参数值是相对于父viewgroup而言的。
调用MarginLayoutParams.setMargins(),重新设置控件位置参数来实现控件移动效果。这种方式比较适合RelativeLayout、FrameLayout,AbsoluteLayout,对于LinearLayout,因为最后增加的控件总在最下或最右,所以达不到移动效果,TableLayout也不行。
通过view.setX() 、view.setY()方式。任意布局上的空间都可以支持这种方式移动,参数值是指相对于自身原本位置X坐标轴和Y坐标轴上的偏移量。
各种实现方式注意事项
方案1:使用View.layout()方式实现FAB按钮的移动
public class FloatButton extends android.support.design.widget.FloatingActionButton implements View.OnTouchListener{ int lastX, lastY; int originX, originY; int screenWidth ; int screenHeight ; int distance; public FloatButton(Context context) { super(context); getScreenWidthAndHeight(context); setOnTouchListener(this); } public FloatButton(Context context, AttributeSet attrs) { super(context, attrs); getScreenWidthAndHeight(context); setOnTouchListener(this); } public FloatButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); getScreenWidthAndHeight(context); setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { int ea = event.getAction(); switch (ea) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX();// 获取触摸事件触摸位置的原始X坐标 lastY = (int) event.getRawY(); originX = lastX; originY = lastY; break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; int l = v.getLeft() + dx; int b = v.getBottom() + dy; int r = v.getRight() + dx; int t = v.getTop() + dy; // 下面判断移动是否超出屏幕 if (l < 0) { l = 0; r = l + v.getWidth(); } if (t < 0) { t = 0; b = t + v.getHeight(); } if (r > screenWidth) { r = screenWidth; l = r - v.getWidth(); } if (b > screenHeight) { b = screenHeight; t = b - v.getHeight(); } v.layout(l, t, r, b); lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); v.postInvalidate(); break; case MotionEvent.ACTION_UP: distance = (int) event.getRawX() - originX + (int)event.getRawY() - originY; break; } return false; } private void getScreenWidthAndHeight(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); screenWidth = dm.widthPixels; screenHeight = dm.heightPixels; }}
优劣:这种方式实现是最简单的的,但是当同一个viewgroup中有控件更新(界面刷新)时,移动的控件会复位,即回到一开始的位置.
仔细观察下图,布局中上方的Banner轮播图和FAB按钮是在同一个viewgroup内的,每当轮播图刷新的时候,FAB按钮就回到了原来的位置,所以在该种情况下,不适合用于view.layout()方式实现位移。解决方法未知……
缺陷展示效果图:
方案2:调用MarginLayoutParams.setMargins()设置View的MarginLeft以及MarginTop属性值从而确立View移动的实时位置。
该方案可参考:Android-满屏幕拖动的控件
注意事项:这种方式比较适合RelativeLayout、FrameLayout,AbsoluteLayout,对于LinearLayout,因为最后增加的控件总在最下或最右,所以达不到移动效果,TableLayout也不行。并且你在XML里设置的一些属性值很可能会影响到View的移动。例如:你在XML文件里为FAB按钮的初始位置设置了 android:layout_marginRight=”xxx” 或者landroid:layout_alignParentBottom=”true”属性值。
方案3:使用view.setX()、view.setY()方式实现FAB按钮的移动
代码来自:安卓可拖拽悬浮按钮二
public class DragFloatActionButton extends FloatingActionButton{ private int parentHeight; //父容器高度 private int parentWidth; //父容器宽度 private int lastX; private int lastY; private boolean isDrag; public DragFloatActionButton(Context context) { super(context); } public DragFloatActionButton(Context context, AttributeSet attrs) { super(context, attrs); } public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { int rawX = (int) event.getRawX(); int rawY = (int) event.getRawY(); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: setPressed(true); //设置该Button状态为摁下 Pressed状态 isDrag = false; //首次摁下时设置 正在拖拽?false //表示子组件要自己消费这次事件,告诉父组件不要拦截(抢走)这次的事件 getParent().requestDisallowInterceptTouchEvent(true); lastX = rawX; lastY = rawY; //记录上次首次触碰到屏幕时Button的坐标 (lastX ,lastY) ViewGroup parent; if (getParent() != null) { //如果有父容器 parent = (ViewGroup) getParent(); parentHeight = parent.getHeight(); //获取容器的高度 parentWidth = parent.getWidth(); //获取容器的宽度 } break; //如果没有父容器直接退出,不实现拖拽移动,限定button只能在有父容器的情况下实现有范围的移动 if (parentHeight <= 0 || parentWidth == 0) { isDrag = false; //不能拖拽 break; //直接退出 } else { isDrag = true; //正在拖拽 } int dx = rawX - lastX; //获取手势X坐标轴上的实时位移量 int dy = rawY - lastY; //获取手势Y坐标轴上的实时位移量 //这里修复一些华为手机无法触发点击事件 int distance = (int) Math.sqrt(dx * dx + dy * dy); if (distance == 0) { isDrag = false; break; //getX()当前view距离父容器左边缘的距离 +dx 手势X坐标实时位移量 = 意图当前view距离父容器左边缘的距离 //getY()当前view距离父容器顶部边缘的距离 +dy 手势Y坐标实时位移量 = 意图view距离父容器顶部边缘的距离 float x = getX() + dx; float y = getY() + dy; //检测是否到达边缘 左上右下 并且限制view的移动范围为其父容器范围//x < 0 表示向左移动超出父容器 //此时设置 x = 0 继续向左拖拽悬浮按钮不移动//x > parentWidth-getWidth() 表示向右移动超出父容器 //此时设置 x = parentWidth-getWidth() //代表往X坐标轴上右方向移动的最大偏移量为 父容器宽度-自身宽度 (因为Button的右边也不能超出屏幕所以减去button宽度)//getY() < 0 表示当前Button超出父容器上方 -> //令 y = 0 回到 父容器正上方//getY()+ getHeight > parentHeight 表示 当前Button超出父容器下方 //令 y = 父容器高度 - Button高度 回到父容器正下方 x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x; y = getY() < 0 ? 0 : getY() + getHeight() > parentHeight ? parentHeight - getHeight() : y; setX(x); //设置当前view距离父容器左边缘的距离 setY(y); // 设置当前view距离父容器左边缘的距离 lastX = rawX; lastY = rawY; break; case MotionEvent.ACTION_UP: if (isDrag) { setPressed(false); } break; } //如果是拖拽则消耗事件,否则正常传递即可。 return isDrag || super.onTouchEvent(event); }
优点:这里实现了FAB按钮的拖拽移动功能,并且不会影响为FAB按钮设置onClick事件,并且即使同一个viewgroup中有控件更新(界面刷新)时,移动的控件也不会复位回到一开始的位置。
没时间写完了。。。明天继续写
还有很多细节要完善。。。。。。
参考:
- Android 动态移动控件实现
- 安卓可拖拽悬浮按钮二
- 实现可拖拽移动的悬浮按钮
- android 实现自由移动的悬浮按钮
- 通过WindowManager实现可以移动的悬浮按钮
- 悬浮按钮的实现
- 自定义悬浮按钮效果实现,带移动效果
- Jquery实现的悬浮按钮,浮现图片
- Android 悬浮按钮的简单实现
- 使用XML实现悬浮的添加按钮
- 微信小程序16---悬浮按钮的实现
- Android 系统级悬浮按钮的实现
- WindowManager实现悬浮窗口&可自由移动的悬浮窗口
- WindowManager实现悬浮窗口&可自由移动的悬浮窗口
- WindowManager实现悬浮窗口&可自由移动的悬浮窗口
- 可移动的悬浮按钮(自定义View)
- 通过WindowsManager实现移动的悬浮窗口
- android 可移动悬浮框的实现
- onTouchEventListener实现悬浮按钮
- 实现悬浮按钮
- Hdoj 1263 水果
- 【matlab 数据处理】excel读取和写出,匹配
- 不做手机控
- Android Studio之bash: gradlew: command not found
- J2EE框架(四)核心设计模式
- 实现可拖拽移动的悬浮按钮
- SpringBoot+AngularJS导出数据库数据到excel
- 用TensorFlow可视化卷积层的方法
- Spring AOP底层实现- JDK动态代理和CGLIB动态代理
- 浅谈 MVP in Android
- css父元素透明度(opacity)对子元素的影响
- 08_c++访问控制和继承
- 工具类
- vivado(1)——创建工程