在上一篇文章中, 浅谈 Android 事件分发机制(一), 简要分析了一下事件分发机制的原理, 总结一下就是事件层层传递, 直到被消费, 原理看似简单, 但是在实际使用过程中, 场景各不相同, 复杂程度也就因产品而异, 这篇文章就通过给 view 加移动来模拟事件分发.
触摸事件
这里涉及到几个与手指触摸相关的常见事件:
坐标系对于单指触控移动来说, 一次简单的交互流程是这样的:
手指落下(ACTION_DOWN) -> 移动(ACTION_MOVE) -> 离开(ACTION_UP)
坐标系
Android 坐标系以手机屏幕左上角的顶点为坐标原点, 从该点向右为 x 轴正方向, 从该点向下为 y 轴正方向. 上图所示, 一次触摸涉及到多种距离的计算, 上图所标注的方法可以分为两类, 一类是 View 提供的方法, 一类是 MotionEvent 提供的方法.
View 提供的:
getTop(): 获取到 view 自身的顶边到其父布局顶边的距离
getLeft(): 获取到 view 自身的左边到其父布局左边的距离
getRight(): 获取到 view 自身的右边到其父布局左边的距离
getBottom(): 获取到 view 自身底边到其父布局顶边的距离
MotionEvent 提供的方法:
getX(): 获取触摸点距离控件左边的距离, 即视图坐标
getY(): 获取触摸点距离控件顶边的距离, 即视图坐标
getRawX(): 获取触摸点距离整个屏幕左边的距离, 即绝对坐标
getRawY(): 获取触摸点距离整个屏幕顶边的距离, 即绝对坐标
知道了以上的知识点后, 基于文章一 http://blog.manjiexiang.cn/blog/6/detail/ 做 view 的移动, 这里还是三个视图 ViewC,ViewGroupB,ViewGroupA
C 添加移动
给 ViewC(蓝色区域)添加移动
onTouchEvent 返回 true, 自身消费事件.
手指按下 MotionEvent.ACTION_DOWN, 记录当前距离控件左边和顶边的距离 lastX,lastY.
手指移动时 MotionEvent.ACTION_MOVE, 获取当前距离控件左边和顶边的距离 x,y, 减去手指按下时记录的距离 lastX,lastY, 计算得到移动的距离, 移动的距离加上 view 距离父布局的距离, 得到相对于父布局的四个点坐标, layout 重新确认位置.
手指离开 MotionEvent.ACTION_UP, 设置 view 距离父布局的 margin, 这边的操作主要是固定 view 的位置, 后续和视图 B 一起移动时可固定位置.
- private int lastX;
- private int lastY;
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int x = (int) event.getX();
- int y = (int) event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- lastX = x;
- lastY = y;
- break;
- case MotionEvent.ACTION_MOVE:
- // 计算移动的距离
- int offsetX = x - lastX;
- int offsetY = y - lastY;
- int l = getLeft() + offsetX;
- int b = getBottom() + offsetY;
- int r = getRight() + offsetX;
- int t = getTop() + offsetY;
- // 重新确认位置
- layout(l, t, r, b);
- break;
- case MotionEvent.ACTION_UP:
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
- params.setMargins(getLeft(), getTop(), 0, 0);
- break;
- default:
- break;
- }
- return true;
- }
B 添加移动
同样给 ViewGroupB 添加以上的代码用于 B 的移动.(蓝色的视图 C,*** 的视图 B)
情况一: 如上图 这里视图 B,C 的 onTouchEvent 都返回 true, 在 C 区域滑动, viewC 消费了事件, 不再传递给 B; 只有在 B,C 不重叠的区域滑动, C 才会移动, 这时没有接触到 C, 所以不会触发 C 的事件. 因为我们在 C 的
MotionEvent.ACTION_UP
手指离开时固定了 C 到父布局 (B) 的距离, 所以 C 相对 B 的位置没变.
情况二: 如上图, 将 C 的 onTouchEvent 返回 false, 在 C 区域滑动, 事件没有消费, 传递给到了 B,B 可以滑动, 在不重叠区域一样可以滑动 B.
如果 B 把事件拦截了 onInterceptTouchEvent 返回 true, 那么效果和情况二相同的, 不管 C 的 onTouchEvent 返回啥, 都响应不了.
这里模拟了视图 B,C 的滑动, A 的话原理相同, 这里就不再描述.
浅谈 Android 事件分发的两篇文章结束了, 这里只是简单描述模拟了事件分发. 日常项目中若是遇到情况怕是更为复杂, 想要彻底玩转事件分发机制还需要进一步的研究.
浅谈 Android 事件分发机制(一)
来源: http://www.bubuko.com/infodetail-2921263.html