# 事件分发机制

## 目录
- [事件分发流程](#事件分发流程)
- [onTouchEvent](#ontouchevent)
- [onInterceptTouchEvent](#onintercepttouchevent)
- [事件冲突处理](#事件冲突处理)
- [事件分发源码分析](#事件分发源码分析)
- [面试常见问题](#面试常见问题)

---

## 事件分发流程

### 分发流程

```
Activity → ViewGroup → View
    ↓         ↓         ↓
dispatchTouchEvent()
    ↓         ↓         ↓
onInterceptTouchEvent() (仅 ViewGroup)
    ↓         ↓         ↓
onTouchEvent()
```

### 关键方法

```java
// 1. dispatchTouchEvent()：分发事件
// 2. onInterceptTouchEvent()：拦截事件（仅 ViewGroup）
// 3. onTouchEvent()：处理事件
```

---

## onTouchEvent

### onTouchEvent 方法

```java
// View.onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
    // 1. 处理点击事件
    if (event.getAction() == MotionEvent.ACTION_UP) {
        performClick();
    }
    // 2. 返回 true 表示消费事件
    return true;
}
```

### 返回值

```java
// true：消费事件，不再向下传递
// false：不消费事件，继续向下传递
```

---

## onInterceptTouchEvent

### onInterceptTouchEvent 方法

```java
// ViewGroup.onInterceptTouchEvent()
public boolean onInterceptTouchEvent(MotionEvent ev) {
    // 返回 true：拦截事件，不传递给子 View
    // 返回 false：不拦截，传递给子 View
    return false;
}
```

### 使用场景

```java
// 例如：ScrollView 拦截滑动事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_MOVE) {
        // 判断是否需要拦截
        if (needIntercept()) {
            return true; // 拦截，自己处理
        }
    }
    return false; // 不拦截，传递给子 View
}
```

---

## 事件冲突处理

### 冲突场景

```java
// 1. ScrollView 和 ViewPager 滑动冲突
// 2. ListView 和 Button 点击冲突
// 3. 嵌套滑动冲突
```

### 解决方法

#### 方法1：外部拦截

```java
// 在父 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：内部拦截

```java
// 在子 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

```java
// 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: 如何解决滑动冲突？

**答案：**
1. **外部拦截**：在父 View 的 onInterceptTouchEvent() 中处理
2. **内部拦截**：在子 View 的 dispatchTouchEvent() 中处理

### Q4: 事件分发的返回值？

**答案：**
- **true**：消费事件，不再传递
- **false**：不消费事件，继续传递

---

*最后更新：2024年*
