之前我们介绍了安卓的四大组件,接下来我们说说Android的事件传递机制,我们都知道,任何的组件都是在一个laytout里进行摆放的。不管是哪种layout,都是继承自ViewGroup容器。而我们所要研究的也就是从activity—viewgroup–XXXview,一级一级的传递。
事件的传递就好像一个任务下来,总有人要做,我们现在分配3个角色,正好对应不同的层级,大boss领导,中层主管,以及下层小兵。进行这么分配也是为方便理解记忆。
首先,我们首先说说时间分发主要用到的方法:
dispatchTouchEvent:用来判断是否进行事件分发
onInterceptTouchEvent:用来判断是否进行事件拦截
onTouchEvent:用来处理事件行为
1、首先我们有应用程序activity:
public class TestMain extends Activity {
private String tag = TestMain.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testevent);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtil.i(tag,"TestMain dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtil.i(tag,"TestMain onTouchEvent");
return super.onTouchEvent(event);
}
}
activity里只有这俩个方法,是因为事件要不要分发,要分发就分发,不分发就自己处理。
2、涉及到是ViewGroup容易,这里的存放的都是子view,这里涉及到一个事件的分发,和是否拦截的问题。
public class TestLinearlayout extends LinearLayout {
String tag = TestLinearlayout.class.getSimpleName();
public TestLinearlayout(Context context) {
super(context);
}
public TestLinearlayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtil.i(tag,"+++++++++TestLinearlayout dispatchTouchEvent ");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
LogUtil.i(tag,"++++++++ TestLinearlayout onInterceptTouchEvent ");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtil.i(tag,"++++++++ TestLinearlayout onTouchEvent ");
return super.onTouchEvent(event);
}
}
这个是layout布局文件,我们引用的时候,就引用当前的layout文件。
3、接下来就是我们自定义的TextView,其实也没啥,就是几个方法。
public class TestTextView extends TextView {
String tag = TestTextView.class.getSimpleName();
public TestTextView(Context context) {
this(context,null);
}
public TestTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
LogUtil.i(tag, "------TestTextView dispatchTouchEvent ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtil.i(tag, "------TestTextView onTouchEvent ");
return true;
}
}
这是我们最里面的布局。
统一的layout为:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<com.android.app.view.TestLinearlayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/myshape"
android:gravity="center"
android:layout_margin="50dp">
<com.android.app.view.TestTextView
android:id="@+id/myview"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/myshape"
android:text="最小组件"
android:gravity="center"/>
</com.android.app.view.TestLinearlayout>
</LinearLayout>
为清晰的显示我们的组件边间,加一个背景shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="@color/colorPrimary" android:width="1px"></stroke>
<solid android:color="@color/white"></solid>
</shape>
页面效果看起来是这个样子的:
注:最外面的就是咱们的activity界面了,中间的边框就是我们layout,里面的框就是我们的textView了。
接下来,我们就开始点击各种事件,看下Event是怎么传递的了。
有一下几种case:
1、首先,我们点击最外面的activity,日志如下:
1·1 都是正常的返回的都是super
I/TestMain: TestMain dispatchTouchEvent
I/TestLineayout: TestLinearlayout dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout onInterceptTouchEvent
I/TestTextView: TestTextView dispatchTouchEvent
I/TestTextView: TestTextView onTouchEvent
I/TestLinearlayout: TestLinearlayout onTouchEvent
I/TestMain: TestMain onTouchEvent
#我们看到事件都是一级一级向下传递,然后在向上传递
1·2如果activity中的dispatchTouchEvent返回true,则打印
I/TestMain: TestMain dispatchTouchEvent
相当于当前累消费了此事件,就是我能干这件事不用下边的人干,不在放下传递
1.3如果activity中的dispatchTouchEvent返回false,则打印:
I/TestMain: TestMain dispatchTouchEvent
相当于当前类消费事件不在往下传递。
2、我们在点击,layout中的参数设置:
2.1 正常super传递,则和1.1一样,事件正常传递
2.2 layout.dispatchTouchEvent 返回ture时,则打印:
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
相当于当前类消费了此事件,不往下下传递。翻译过来就是领导先不干,让下边的人干, 结果主管一看,擦,自己不干,也没让下面的人干,也没返给领导到底干了没干。
2.3 layout.dispatchTouchEvent,如果返回false,则打印:
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
I/TestMain: TestMain onTouchEvent
相当于当前累没有消费此事件,但是不在继续分发,直接返回让父类消费。翻译过来就是领导先不干,让下边的人干, 结果主管一看也干不了,那下面的人就更干不了了,返回给领导,领导说,擦,这帮废物,还得我自己干,执行容器ontouchevent方法
2.4 layout.dispatchTouchEvent 返回super类,继续向下传递,但layout.onInterceptTouchEvent 返回true,则打印:
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout onInterceptTouchEvent
I/TestLinearlayout: TestLinearlayout onTouchEvent
I/TestMain: TestMain onTouchEvent
此类有能力消费了此事件,拦截,不往下下传递。翻译过来就是领导先不干,让下边的人干, 结果主管一看,汗,简单,能干,那就拦截,自己先处理了这件事, layout.touch事件就是主管在处理事情,如果处理成功, 返回true,就不会执行父类的ontouch方法,就是我处理了,你不用在处理了,如果返回false,相当于我处理了一顿结果还没处理成。还得让父类解决,就继续执行父类的onTouchevent方法,即TestMain onTouchEvent。(此时:TestLinearlayout onTouchEvent方法返回false和super的作用一致,都是我处理不了)
2.5 类还原初始化,layout.dispatchTouchEvent 返回super类,继续向下传递,但layout.onInterceptTouchEvent 返回false,则打印:
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout onInterceptTouchEvent
I/TestTextView: TestTextView dispatchTouchEvent
I/TestTextView: TestTextView onTouchEvent
I/TestLinearlayout:TestLinearlayout onTouchEvent
I/TestMain: TestMain onTouchEvent
此类暂时不知道能不能干,继续向下传递,让手下先干的,我不进行拦截。看下面的人办的怎么样。(因为我们都是返回的super,在这里我们可以认为手下处理不了,返回主管处理不了,返回领导,让领导处理,这就是打印的全过程)。
3、textview中的参数设置
3.1 正常super传递,则和1.1一样,事件正常传递
3.2 textview.dispatchTouchEvent 返回ture时,则打印
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout onInterceptTouchEvent
I/TestTextView: TestTextView dispatchTouchEvent
此类消费了事件,不进行传递,和2.2一样,只不过角色换成了手下,你说气不气人,你干成干不成总有个回话了吧,上头也不知道个结果
3.3 textview.dispatchTouchEvent 返回false时,则打印:
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
I/TestLinearlayout:TestLinearlayout onInterceptTouchEvent
I/TestTextView:TestTextView dispatchTouchEvent
I/TestLinearlayout:TestLinearlayout onTouchEvent
I/TestMain: TestMain onTouchEvent
即当前类没有消费此事件,返回给父类消费,翻译过来就是:分给我了任务,我一看结局不了,立马返给主管,让主管解决,主管解决的情况如何看2.4layout.touchevent 返回解释。
3.4 textview.dispatchTouchEvent 返回super,ontouch事件返回true时,则打印:
I/TestMain: TestMain dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout dispatchTouchEvent
I/TestLinearlayout: TestLinearlayout onInterceptTouchEvent
I/TestTextView: TestTextView dispatchTouchEvent
I/TestTextView: TestTextView onTouchEvent
即当前类有能力消费此事件。不在向上级传递。翻译过来就是:领导先不干,传给主管,主管懒的看也不看,一下传给手下,手下一看,这么简单,我能处理,就处理了,返回true。就是我处理了此事,你们不用再处理了,如果返回false,就是说我处理了一顿也没处理,在返给主管吧,主管的处理情况见2.4解释翻译。
好了,Android之View事件机制说了这么多,估计大家也有点绕。自己敲的代码自己亲自测试下。增进下了解与记忆,并且结合源码,自己实践下。我也不放整个项目了,就放几个简单的文件,方便下懒人大神吧。
View的机制是安卓中的难点,这个得慢慢消化,并且这方面的知识在面试中也是经常问到的问题。还需大家要多多研究啊!
2017年第一份知识记录,好几天没写了,这几天赶紧补上。也㊗️大家2017技术上多多增进,知识慢慢积累。早日铸就高塔。