前言
懵懵懂懂搞了几年安卓,对于事件分发这一块,总是感觉没有完全理解清楚。看了很多技术文章,自信感觉已经差不多了,然后一到面试,感觉总是说不出来。然后换了一套方式来理解,然后发现,事件分发也就是那么回事。
举例理解
举一个例子来说明,某天你老板看到个比较难的效果觉得很炫酷,然后就和产品经理随口给派发了一个任务,产品经理把这个任务又给派发到组长。组长是个搞技术的,虽然自己有能力解决一些问题,但是发现自己有小弟,当仁不让,任务给了程序员。然后程序员发现,整个技术部就我最小,我没有小弟,然后只能苦逼的看任务。然后一看,发现自己搞不了。老老实实和组长说干不了。然后组长也就一推四五六,就和产品经理说做不了,直接报告给老板吧,这太难了。算了把,凑合凑合现在的也能用。老板听取到产品经理的建议以后放弃了任务。然后这次任务流程可以概括为下面的图。(ps:不经过任何拦截的流程)
上述图是不经过任何拦截的流程,在事实上,也会有很多种可能:
- 老板发布的任务太难,产品经理一看,立即劝导老板不要做这个,然后事件终止
- 组长是个比较会照顾人的或者说知道程序员是个新手干不了,直接给完成了任务,然后反馈给产品经理,然后事件终止
- 程序员干不了,又给组长,组长能力强给干完了,然后反馈给产品经理,事件终止
- 程序员能力比较强直接给做了,然后报告给组长、组长又给产品经理然后然后事件终止了
- 程序员能力特强,完成以后给组长,组长一看直接给老板,然后事件就到此终止了
上面描述了安卓的事件分发机制和处理机制,然后我们归类一下方法
activity:
(1)dispatchTouchEvent 该方法用来分发事件,一般不会重写这个方法 (给任务给组长)
(2)onTouchEvent 用来处理事件,常见重写(当返回为true: 劝导老板)
viewGroup:
(1)dispatchTouchEvent 该方法用来分发事件,一般不会重写这个方法 (给任务给程序员)
(2)onInterceptTouchEvent 拦截事件(想不想做)
(2)onTouchEvent 用来处理事件,常见重写(当返回为true: 自己做 | 接到程序员求助然后做)
view:
(1)dispatchTouchEvent 该方法用来分发事件,一般不会重写这个方法 (判定自己是否能做)
(2)onTouchEvent 用来处理事件,常见重写(当返回为true: 自己做)
代码验证
分别编写activity、viewGroup、view进行验证:
新建一个项目,先来写ActivityA,代码如下:
public class ActivityA extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) { //判定是否为点击
Log.e("测试--Down----->", "ActivityA:dispatchTouchEvent");
} else if (ev.getAction() == MotionEvent.ACTION_UP) { //判定是否为擡起
Log.e("测试--UP------->", "ActivityA:dispatchTouchEvent");
} else {
Log.e("测试--Other---->", "ActivityA:dispatchTouchEvent");
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("测试--Down----->", "ActivityA:onTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("测试--UP------->", "ActivityA:onTouchEvent");
} else {
Log.e("测试--Other---->", "ActivityA:onTouchEvent");
}
return super.onTouchEvent(event);
}
}
同理,编写ViewGroupB,如下
public class ViewGroupB extends LinearLayout {
public ViewGroupB(Context context) {
super(context);
}
public ViewGroupB(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupB(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("测试--Down----->","ViewGroupB:dispatchTouchEvent");
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.e("测试--UP------->","ViewGroupB:dispatchTouchEvent");
} else {
Log.e("测试--Other---->","ViewGroupB:dispatchTouchEvent");
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("测试--Down----->","ViewGroupB:onInterceptTouchEvent");
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
Log.e("测试--UP------->","ViewGroupB:onInterceptTouchEvent");
} else {
Log.e("测试--Other---->","ViewGroupB:onInterceptTouchEvent");
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("测试--Down----->","ViewGroupB:onTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("测试--UP------->","ViewGroupB:onTouchEvent");
} else {
Log.e("测试--Other---->","ViewGroupB:onTouchEvent");
}
return super.onTouchEvent(event);
}
}
再编写ViewC,代码如下
public class ViewC extends View {
public ViewC(Context context) {
super(context);
}
public ViewC(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("测试--Down----->","ViewC:dispatchTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("测试--UP------->","ViewC:dispatchTouchEvent");
} else {
Log.e("测试--Other---->","ViewC:dispatchTouchEvent");
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("测试--Down----->","ViewC:onTouchEvent");
} else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.e("测试--UP------->","ViewC:onTouchEvent");
} else {
Log.e("测试--Other---->","ViewC:onTouchEvent");
}
return super.onTouchEvent(event);
}
}
将其写入嵌套在一起,activity_test.xml。代码如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorFE6016"
android:gravity="center"
android:orientation="vertical">
<com.hysf.ym.custorm.meView.fenfa.ViewGroupB
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@color/color7F6F51"
android:gravity="center">
<com.hysf.ym.custorm.meView.fenfa.ViewC
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@color/colorFD9C1A" />
</com.hysf.ym.custorm.meView.fenfa.ViewGroupB>
</RelativeLayout>
得到的效果如下:
最外面橘色的是ActivityA,大一点的灰色的是ViewGroupB,中间偏黄色的是ViewC,现在我们点击ViewC ,得到如下流程:
从打印的结果,我们可以很清楚到看到事件的流程:
首先ActivityA得到了事件,由它的dispatchTouchEvent方法来分发事件,传给了ViewGroupB,然后由于ViewGroupB的onInterceptTouchEvent方法没有做出拦截,因此事件传递给了ViewC,而ViewC的onTouchEvent没有处理,就又调用给ViewGroupB,继ViewGroupB的onTouchEvent也没有处理,然后传给ActivityA的onTouchEvent方法,然后按下操作的流程就结束了,至于up事件又调用了ActivityA的dispatchTouchEvent和onTouchEvent,我们稍后再说。
实验验证:
ActivityA
测试1:单独设置ActivityA的onTouchEvent方法返回值设置为true,测试结果如下
测试2:单独设置ActivityA的onTouchEvent方法返回值设置为false,测试结果如下:
ViewGroudB:
测试1:单独设置ViewGroupB的onInterceptTouchEvent方法返回值设置为true,测试结果如下:
在测试1的基础上,设置ViewGroupB的onTouchEvent方法返回值设置为true,测试结果如下:
在测试1的基础上,设置ViewGroupB的onTouchEvent方法返回值设置为false,测试结果如下:
测试2:单独设置ViewGroupB的onInterceptTouchEvent方法返回值设置为false,测试结果如下:
在测试2的基础上,设置ViewGroupB的onTouchEvent方法返回值设置为true,测试结果如下:
在测试2的基础上,设置ViewGroupB的onTouchEvent方法返回值设置为false,测试结果如下:
ViewC
测试1:单独设置ViewC的onTouchEvent方法返回值设置为true,测试结果如下
测试2:单独设置ViewC的onTouchEvent方法返回值设置为false,测试结果如下:
总结:
- ACTION_DOWN可以理解成所有事件的一种,或者理解成探路的
- 当ACTION_DOWN判断好具体是怎么dispatch,怎么消费的以后,对应的ACTION_MOVE和ACTION_UP就会按照消费的顺序,进行执行
- 当然,如果中间有拦截,也是按消费的路线进行
- 对于事件的拦截,我们主要重写就是OnInterceptTouchEvent和onTouchEvent方法。两句就可以总结:
(1)对于事件的传递,返回结果为true,表示拦截,不再往下传递,为false,不拦截,继续往下传递。主要针对的就是 OnInterceptTouchEvent方法。
(2)对于事件的处理,返回结果为true,表示拦截,不再往上传递(即我处理的很完美,不需要你再来审核我!),返回结果为 false(没有成功处理事件),继续向上传递。(onTouchEvent方法。)