Android View事件机制
1.事件机制代码解释说明
1.1 触摸事件
1 | /** |
1.2 分发事件
1 | /** |
1.3 拦截事件
1 | /** |
2.三个事件机制怎么向其调用者传递处理结果
- 1、事件的分发机制,dispatchTouchEvent。
- true-事件被以该节点为根节点的View树成功处理,此时该事件就算是处理完成了,事件不会再向上返还给View的父节点(把事件分发过来的那个节点)。
- false-以该节点为根节点的View树种,没有一个View(包括该View)成功处理了此事件,所以事件会向上返还给View的父节点(把事件分发过来的那个节点)。
- 2、事件的拦截机制,onInterceptTouchEvent。主要是parent根据它内部的状态、或者child的状态,来把事件拦截下来,阻止其进一步传递到child的机制。
- true-当前ViewGroup(因为View中没有该方法,而没有child的VIew也不需要有拦截机制)希望该事件不再传递给其child,而是希望自己处理。
- false-当前ViewGroup不准备拦截该事件,事件正常向下分发给其child。
- 3、事件的处理机制,onTouchEvent。主要是事件序列的接受者(可以是一个View或者ViewGroup),对事件作出处理,并且向其parent传递处理结果的机制。
- true-表示该View成功处理了该事件,该处理结果会向上通知给其parent。
- false-表示该View没有成功处理该事件,那么它的parent会有机会来处理该事件(parent标记为事件序列接受者,parent 的 onTouchEvent 在 Down 事件时返回true)。
3.触摸事件类型【常见】
a.1 ACTION_DOWN
- 用户手指的按下操作,一次按下的操作标志者一次触摸事件的开始。
a.2 ACTION_UP
- 用户手指离开屏幕的操作,一次抬起操作标志者一次触摸事件的结束
a.3 ACTION_MOVE
- 用户手指按压屏幕后,在松开之前,如果距离超过一定得阈值,那么会被判定为ACTION_MOVE。一般情况下,手指的轻微移动都会触发一系列的移动事件。
a.4 注意
- 1.在一次屏幕触摸事件中,ACTION_DOWN和ACTION_UP者两个事件是必须的,而ACTION_MOVE视情况而定,如果用户仅是点击了一下屏幕,那么可能只会监测到按下和抬起的动作。
- 2.通过MotionEvent可以获得事件发生的x和y坐标,getX和getY返回的是相当于当前View左上角的X和Y坐标,getRawX和getRawY返回的是相当于手机屏幕左上角的X和Y坐标
a.5 其他
1
2
3
4
5
6
7
8
9
10
11
12Touch触摸事件
♦ 在Android中Touch触摸事件主要包括:
点击(onClick)
长按(onLongClick)
拖拽(onDrag)
滑动(onScroll)
♦ 在Android中Touch操作状态主要包括:
按下(ACTION_DOWN)
移动(ACTION_MOVE)
抬起(ACTION_UP)
取消手势(ACTION_CANCEL)
划出屏幕(ACTION_OUTSIDE)Android触摸事件流程总结
- 1.一个事件序列从手指触摸屏幕开始,到触摸结束。同一事件序列是以
ACTION_DOWN开始,中间有数量不定的ACTION_MOVE事件,最终以ACTION_UP结束 - 2.事件传递顺序是:Activity——>Window——>View;最后顶级View接收到事件后,就会按照事件分发机制去分发事件
- 3.事件传递过程是由外向内的,即事件总是有父元素分发给子元素
- 1.一个事件序列从手指触摸屏幕开始,到触摸结束。同一事件序列是以
4.三个重要方法的分布情况
- 4.1 View
- 1.分发事件 dispatchTouchEvent
- 2.处理事件 onTouchEvent
- 4.2 ViewGroup
- 1.分发事件 dispatchTouchEvent
- 2.拦截事件 onInterceptTouchEvent
- 3.处理事件 onTouchEvent
- 4.3 Activity
- 1.分发事件 dispatchTouchEvent
- 2.处理事件 onTouchEvent
- 1.分发事件 dispatchTouchEvent
5.滑动冲突的思路及方法
5.1 第一种情况,滑动方向不同
- 5.1.1 ScrollView里嵌套ViewPager
5.2 第二种情况,滑动方法相同
- 5.2.1 ViewPager嵌套ViewPager
- 5.2.2 ScrollView嵌套RecyclerView或者GridView
5.3 第三种情况,以上两种情况嵌套
- 5.3.1 比如scrollview嵌套recyclerview,recyclerView复杂条目中又有横向滑动的list
6.滑动冲突解决方案
6.1 滑动方向不同之以ScrollView与ViewPager为例的外部解决法
- 从 父View 着手,重写 onInterceptTouchEvent 方法,在 父View 需要拦截的时候拦截,不要的时候返回false,代码大概如下
1 | 举例子:以ScrollView与ViewPager为例 |
6.2 滑动方向不同之以ScrollView与ViewPager为例的内部解决法
从子View着手,父View 先不要拦截任何事件,所有的 事件传递给子View,如果子View需要此事件就消费掉,不需要此事件的话就交给 父View 处理。
实现思路 如下,重写 子View 的dispatchTouchEvent方法,在Action_down动作中通过方法requestDisallowInterceptTouchEvent(true) 先请求 父View 不要拦截事件,这样保证子View能够接受到Action_move事件,再在Action_move动作中根据自己的逻辑是否要拦截事件,不要的话再交给 父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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50public class MyViewPager extends ViewPager {
private static final String TAG = "yc";
int lastX = -1;
int lastY = -1;
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getRawX();
int y = (int) ev.getRawY();
int dealtX = 0;
int dealtY = 0;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
dealtX = 0;
dealtY = 0;
// 保证子View能够接收到Action_move事件
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
dealtX += Math.abs(x - lastX);
dealtY += Math.abs(y - lastY);
Log.i(TAG, "dealtX:=" + dealtX);
Log.i(TAG, "dealtY:=" + dealtY);
// 这里是够拦截的判断依据是左右滑动,可根据自己的逻辑进行是否拦截
if (dealtX >= dealtY) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}
}
6.3 滑动方向相同之以ViewPager嵌套ViewPager为例的内部解决法
1 | 从 子View ViewPager着手,重写 子View 的 dispatchTouchEvent方法,在 子View 需要拦截的时候进行拦截,否则交给 父View 处理,代码如下 |
6.4 滑动方向相同之以ScrollView和RecycleView为例的内部解决法
1 | public class RecyclerScrollview extends ScrollView { |