Android面试——事件的传递机制
来源:互联网 发布:连接其它机器的mysql 编辑:程序博客网 时间:2024/06/07 19:23
Android事件的传递机制
前言
最近面试了很多公司,大多数公司都问到了这么一个问题,就是Android事件的传递机制,那Android事件的传递机制到底是怎么一回事?今天我们来探讨探讨!
正文
Android中三个方法是关于事件传递的,分别是dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent,那这三个方法分别有什么用呢?其实我们可以从字面上的意思可以理解分别是分配触摸事件、截断触摸事件以及响应触摸事件,那这三个方法到底在事件的传递机制中扮演着什么角色?我们一起可以来看看源码!在看源码前,我们先明确事件的基本传递顺序和哪些类中有哪些触摸事件的方法!(后面会具体证明)传递顺序是从最外层传递到最里层,例如:Activity - - > LinearLayout - - > TextView,下面是哪些类中有哪些事件方法
其中onInterceptTouchEvent只有ViewGroup有,为什么ViewGroup才有onInterceptTouchEvent方法,因为他是截断事件,而截断事件不可能存在Activity中与View中,在Activity中,那你截取触摸事件干什么呢?在Activity中将事件截取了,那Activity中的布局控件就获取不到触摸事件,那相当于没有布局上控件的什么事!所以Activity中Google官方没有给Activity设置onInterceptTouchEvent方法,那View中为什么没有该方法呢?那是因为View本来就是最底层了,View没有下一层的子控件了,不需要向下传递事件,你截取事件与不截取事件都是一样的,所以也没有onInterceptTouchEvent的方法!
下面我们重写三个类,分别是FrameLayout、LinearLayout、TextView
FrameLayout
public class MyFrameLayout extends FrameLayout{ public MyFrameLayout(@NonNull Context context) { super(context); } public MyFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Logger.e("Leezp", "MyFrameLayout调用dispatchTouchEvent分配任务"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Logger.e("Leezp", "MyFrameLayout调用onInterceptTouchEvent是否阻止任务?"); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Logger.e("Leezp", "MyFrameLayout调用onTouchEvent是否处理了任务?"); return super.onTouchEvent(event); }}
LinearLayout
public class MyLinearLayout extends LinearLayout{ public MyLinearLayout(Context context) { super(context); } public MyLinearLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Logger.e("Leezp", "MyLinearLayout调用dispatchTouchEvent分配任务"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Logger.e("Leezp", "MyLinearLayout调用onInterceptTouchEvent是否阻止任务?"); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Logger.e("Leezp", "MyLinearLayout调用onTouchEvent是否处理了任务?"); return super.onTouchEvent(event); }}
TextView
public class MyTextView extends android.support.v7.widget.AppCompatTextView{ public MyTextView(Context context) { super(context); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Logger.e("Leezp", "MyTextView调用dispatchTouchEvent分配任务"); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Logger.e("Leezp", "MyTextView调用onTouchEvent是否处理了任务?"); return super.onTouchEvent(event); }}
然后在MainActivity中将其所对用的dispatchTouchEvent、onTouchEvent两个方法重写一下
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) { Logger.e("Leezp", "MainActivity调用dispatchTouchEvent分配任务"); return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) { Logger.e("Leezp", "MainActivity调用onTouchEvent是否处理了处理任务?"); return super.onTouchEvent(event);}
这儿的Logger是我自己写的一个Log日志工具,你们可以直接用Log代替!
然后在activity_main布局中
<?xml version="1.0" encoding="utf-8"?><com.android.leezp.toucheventtransfer.utils.MyFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorAccent" tools:context="com.android.leezp.toucheventtransfer.activities.MainActivity"> <com.android.leezp.toucheventtransfer.utils.MyLinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:layout_gravity="center" android:background="@color/colorPrimary" android:gravity="center" android:orientation="vertical"> <com.android.leezp.toucheventtransfer.utils.MyTextView android:layout_width="match_parent" android:layout_height="100dp" android:background="@color/colorBlack" android:gravity="center" android:text="事件传递" android:textColor="#ffffff" android:textSize="15sp" /> </com.android.leezp.toucheventtransfer.utils.MyLinearLayout></com.android.leezp.toucheventtransfer.utils.MyFrameLayout>
展示图:
最外层红色的是个FrameLayout,蓝色的是LinearLayout,黑色的是TextView
然后我们运行该程序,点击其中黑色区域
我们可以看见事件的经过,但是事件怎么调用了两次MainActivity的dispatchTouchEvent的方法呢?
其实我们的事件分三种,一种是down,手指接触屏幕,一种是move,手指在屏幕上移动,一种是up,手指离开屏幕,而我们点击事件只有down与up,而在down的过程中,事件会依次去按照传递过程中的步骤从最外层到最里层,然后获取到他这个事件在哪一层被处理了,然后下一次的up事件就只传到该层处理就行了,不需要继续传递,因为这次down事件所有的类都没处理,所以up事件就传到最外层的MainActivity中,然后进行判断是否处理该事件就结束了,这就是一次点击事件怎么调用了两次MainActivity的dispatchTouchEvent的方法
然后我们来看一下Activity中dispatchTouchEvent方法的源码
/** * Called to process touch screen events. You can override this to * intercept all touch screen events before they are dispatched to the * window. Be sure to call this implementation for touch screen events * that should be handled normally. * * @param ev The touch screen event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
我们可以看到,他会先去判断事件的动作是否是down,如果是down的话,我们可以通过重写onUserInteraction中写入自己的一些界面交互的提示之类的信息,下面是Activity中自带的onUserInteraction方法
/** * Called whenever a key, touch, or trackball event is dispatched to the * activity. Implement this method if you wish to know that the user has * interacted with the device in some way while your activity is running. * This callback and {@link #onUserLeaveHint} are intended to help * activities manage status bar notifications intelligently; specifically, * for helping activities determine the proper time to cancel a notfication. * * <p>All calls to your activity's {@link #onUserLeaveHint} callback will * be accompanied by calls to {@link #onUserInteraction}. This * ensures that your activity will be told of relevant user activity such * as pulling down the notification pane and touching an item there. * * <p>Note that this callback will be invoked for the touch down action * that begins a touch gesture, but may not be invoked for the touch-moved * and touch-up actions that follow. * * @see #onUserLeaveHint() */ public void onUserInteraction() { }
然后就是他会通过superDispatchTouchEvent方法去Activity布局中分配事件,也就是调用子控件的dispatchTouchEvent方法,如果有控件处理了这个事件,他就会返回true,如果没有,它会调用自己的onTouchEvent方法,这就是Activity中的事件处理机制
ViewGroup中的事件处理机制大体差不多,不过代码有点多,如果想深入研究的,也可以看看,也就是父级控件先去调用ViewGroup的dispatchTouchEvent方法,然后判断onInterceptTouchEvent是否截断该事件,如果截断不往下传递,然后调用自己的onTouchEvent方法,如果没有截取该事件,那就继续往下传!
View就更不用说了,也是父级控件调用他的dispatchTouchEvent方法,在dispatchTouchEvent中调用自己的onTouchEvent方法!
总结
dispatchTouchEvent方法就是分配任务的一个方法,如果是Activity,他会先去调用子级的dispatchTouchEvent,如果是ViewGroup,他会先调用自己的onInterceptTouchEvent方法,然后再调用子级的dispatchTouchEvent,不过Activity、ViewGroup、View在这个方法中都有个共同的特征,如果子级没处理,都会调用自己的onTouchEvent方法
onInterceptTouchEvent方法就是ViewGroup进行截断事件向下传递的方法
onTouchEvent方法就是类对事件处理的方法
事件传递机制是从父级到子级一层一层往下传,截断了该事件就不会往下传了,然后会一层一层的返回是否已经处理了该事件了,如果处理了,需返回true,没处理会返回false,所以面试官有时会问你如果截取了事件,是否往下传,这当然是不会往下传了,还可能问你那我截取了事件并处理了该事件,那还会返回父级吗?当然会返回,因为父级要确认是否子级已经处理该事件!
参考:
http://blog.csdn.net/morgan_xww/article/details/9372285/
http://www.cnblogs.com/linjzong/p/4191891.html
- Android面试——事件的传递机制
- Android的事件传递机制
- Android事件的传递机制
- Android 事件的传递机制
- Android 事件的传递机制
- Android事件的传递机制
- Android的事件传递机制
- android的事件传递机制
- Android的事件传递机制
- Android的事件传递机制
- Android的事件传递机制
- Android中事件处理机制之——ViewGroup的事件传递详解(一)
- android 事件传递机制
- android 事件传递机制
- android事件传递机制
- Android事件传递机制
- Android 事件传递机制
- Android事件传递机制
- Qt TCP之Server/Client/Socket信号、槽等总结
- PS技巧二--------钢笔工具制作炫光
- 新人上手TensorFlow 之 Normalization
- python子类调用父类的方法
- 【MyEclipse】myeclipse和mysql的字符编码都设置为utf-8,插入中文依然显示问号
- Android面试——事件的传递机制
- Linux:信号量
- 浅谈Java中static的使用
- 设计模式之pimpl惯用法C++版
- Educational Codeforces Round 24 D. Multicolored Cars 补题
- 内存泄漏检测和静态代码分析等工具简单列举
- Java中的HashCode跟equals重写标准类型
- c语言泛式编程简单入门。
- SUN SPARC T4-4电源故障引起的宕机