4.5 KiB
4.5 KiB
事件分发机制
目录
事件分发流程
分发流程
Activity → ViewGroup → View
↓ ↓ ↓
dispatchTouchEvent()
↓ ↓ ↓
onInterceptTouchEvent() (仅 ViewGroup)
↓ ↓ ↓
onTouchEvent()
关键方法
// 1. dispatchTouchEvent():分发事件
// 2. onInterceptTouchEvent():拦截事件(仅 ViewGroup)
// 3. onTouchEvent():处理事件
onTouchEvent
onTouchEvent 方法
// View.onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
// 1. 处理点击事件
if (event.getAction() == MotionEvent.ACTION_UP) {
performClick();
}
// 2. 返回 true 表示消费事件
return true;
}
返回值
// true:消费事件,不再向下传递
// false:不消费事件,继续向下传递
onInterceptTouchEvent
onInterceptTouchEvent 方法
// ViewGroup.onInterceptTouchEvent()
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 返回 true:拦截事件,不传递给子 View
// 返回 false:不拦截,传递给子 View
return false;
}
使用场景
// 例如:ScrollView 拦截滑动事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
// 判断是否需要拦截
if (needIntercept()) {
return true; // 拦截,自己处理
}
}
return false; // 不拦截,传递给子 View
}
事件冲突处理
冲突场景
// 1. ScrollView 和 ViewPager 滑动冲突
// 2. ListView 和 Button 点击冲突
// 3. 嵌套滑动冲突
解决方法
方法1:外部拦截
// 在父 View 的 onInterceptTouchEvent() 中处理
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false; // 不拦截 DOWN
break;
case MotionEvent.ACTION_MOVE:
if (needIntercept()) {
intercepted = true; // 拦截 MOVE
}
break;
case MotionEvent.ACTION_UP:
intercepted = false; // 不拦截 UP
break;
}
return intercepted;
}
方法2:内部拦截
// 在子 View 的 dispatchTouchEvent() 中处理
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (needParentHandle()) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
return super.dispatchTouchEvent(event);
}
事件分发源码分析
dispatchTouchEvent
// ViewGroup.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
// 1. 检查是否拦截
boolean intercepted = onInterceptTouchEvent(ev);
if (!intercepted) {
// 2. 不拦截,分发给子 View
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (child.dispatchTouchEvent(ev)) {
return true; // 子 View 消费了事件
}
}
}
// 3. 子 View 不消费,自己处理
return onTouchEvent(ev);
}
面试常见问题
Q1: 事件分发流程?
答案: Activity → ViewGroup → View
- dispatchTouchEvent():分发事件
- onInterceptTouchEvent():拦截事件(仅 ViewGroup)
- onTouchEvent():处理事件
Q2: onInterceptTouchEvent 的作用?
答案:
- 决定是否拦截事件
- 返回 true:拦截,不传递给子 View
- 返回 false:不拦截,传递给子 View
Q3: 如何解决滑动冲突?
答案:
- 外部拦截:在父 View 的 onInterceptTouchEvent() 中处理
- 内部拦截:在子 View 的 dispatchTouchEvent() 中处理
Q4: 事件分发的返回值?
答案:
- true:消费事件,不再传递
- false:不消费事件,继续传递
最后更新:2024年