在 APP 中,触摸/点击是一个非常频繁的交互;当控件区域重叠时,想要达到满意的效果,就需要把 Touch 的相关事件理解透彻。

相关方法:

1
2
3
dispatchTouchEvent(MotionEvent ev) // 事件分发
onInterceptTouchEvent(MotionEvent ev) // 事件拦截
onTouchEvent(MotionEvent ev) // 事件响应

其中 ViewGroup 包含以上3个方法,View 不包含onInterceptTouchEvent(毕竟 View 已经是视图控件的最小单元了,无需拦截)。

Touch 事件分析

事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

Father->Child 的传递顺序

  • return true时,touch事件终结于此,并停止向下传递

事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

  • return true时,则表示将事件进行拦截,子View不再处理Touch事件

事件响应:public boolean onTouchEvent(MotionEvent ev)

Child-> Father 的顺序,若中途被消费,父控件不再执行onTouchEvent。

  • return true 则会接收并消费该事件。

DEMO

重写 ViewGroup和View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class FatherView extends RelativeLayout 
{
public FatherView(Context context) {
super(context);
}

public FatherView(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean ret = super.dispatchTouchEvent(ev);
Log.i("TouchDemo", getTag() + " - " + "dispatchTouchEvent: " + ret);
return ret;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean ret = super.onInterceptTouchEvent(ev);
Log.i("TouchDemo", getTag() + " - " + "onInterceptTouchEvent: " + ret);
return ret;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean ret = super.onTouchEvent(ev);
Log.i("TouchDemo", getTag() + " - " + "onTouchEvent: " + ret);
return ret;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ChildView extends View 
{
public ChildView(Context context) {
super(context);
}

public ChildView(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean ret = super.dispatchTouchEvent(ev);
Log.i("TouchDemo", getTag() + " - " + "dispatchTouchEvent: " + ret);
return ret;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean ret = super.onTouchEvent(ev);
Log.i("TouchDemo", getTag() + " - " + "onTouchEvent: " + ret);
return ret;
}
}

布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gif="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#c1e1f8" >

<com.fangqk.touchdemo.FatherView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="#ffffff"
android:tag="Father" >

<com.fangqk.touchdemo.ChildView
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
android:background="#88ff0000"
android:tag="Child-1" />
<com.fangqk.touchdemo.ChildView
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:background="#8800ff00"
android:tag="Child-2" />
</com.fangqk.touchdemo.FatherView>
</RelativeLayout>

说明


预览图中,白色区域是Father,红色区域是Child-1,绿色区域是Child-2。

点击白色区域时

1
2
3
4
# 默认状态
Father - onInterceptTouchEvent: false
Father - onTouchEvent: false
Father - dispatchTouchEvent: false

点击红色区域时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 默认状态
Father - onInterceptTouchEvent: false
Child-1 - onTouchEvent: false
Child-1 - dispatchTouchEvent: false
Father - onTouchEvent: false
Father - dispatchTouchEvent: false

# 使 Father 的 dispatchTouchEvent返回true时
# 不再执行其余响应操作
Father - dispatchTouchEvent: true
Father - dispatchTouchEvent: true

# 使 Father 的 dispatchTouchEvent返回true时
# 不再向Child传递Touch事件
Father - onInterceptTouchEvent: true
Father - onTouchEvent: false
Father - dispatchTouchEvent: false

# 使 Child 的 onTouchEvent返回true时
# Father 的onTouchEvent不再执行
Father - onInterceptTouchEvent: false
Child-1 - onTouchEvent: true
Child-1 - dispatchTouchEvent: true
Father - dispatchTouchEvent: true
Father - onInterceptTouchEvent: false
Child-1 - onTouchEvent: true
Child-1 - dispatchTouchEvent: true
Father - dispatchTouchEvent: true

点击红绿重叠区域时

1
2
3
4
5
6
7
8
# 默认状态
Father - onInterceptTouchEvent: false
Child-2 - onTouchEvent: false
Child-2 - dispatchTouchEvent: false
Child-1 - onTouchEvent: false
Child-1 - dispatchTouchEvent: false
Father - onTouchEvent: false
Father - dispatchTouchEvent: false

关于同级 View 重叠的问题,未来再做探讨

参考链接:
http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html
https://developer.android.com/training/gestures/viewgroup.html