Android事件处理(二)——View的dispatchTouchEvent 函数源码详解

作者: 林子木
博客地址:http://blog.csdn.NET/wolinxuebin


文章意图:

主要是想一自己阅读代码后的一些小收获分享给大家。让大家更加深入的了解Android的事件分发这块的内容。

文章主要内容:

本文将在第一章通过自己的语言,简单介绍View的dispatchTouchEvent,并将其中的一些关键点直接提炼出来,方便那些不想阅读源代码的同学把握住其中的关键点。

在第二章将放上源码,其中包含了我阅读过程中的10处注释。


第一章、View的dispatchTouchEvent关键点提炼

相对与Android事件处理(一)——ViewGroup的dispatchTouchEvent 函数源码详解,View的dispatchTouchEvent简单很多,主要就两点:

1、将事件派发给我们使用setOnTouchListener设置的OnTouchListener的 onTouch 处理;
2、如果1没有处理该事件(也就是没有返回true),那么就将事件派发给View本身的 onTouchEvent 函数处理;
简单的说,就是优先派发给我们设置的Listener,在给view自身的。

第二章、View的dispatchTouchEvent源码及分析

具体的源码及相应的注释如下:
    public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        //[lxb #1] 辅助功能,暂时可以不用理会
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;
        //[lxb #2] 调试使用的,不需要理会
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            //[lxb #3] Android的NestedScrolling机制,初学者查学习事件分发源码可以先滤过这部分
            //[lxb #3] 可以查看http://blog.csdn.net/lmj623565791/article/details/52204039 做一个初步的了解
            //[lxb #3] https://segmentfault.com/a/1190000002873657
            stopNestedScroll();
        }

        //[lxb #4] 如果窗口标记了FILTER_TOUCHES_WHEN_OBSCURED这个,
        //[lxb #4] 将不会进行下发;可以避免一个窗口盖在当前的窗口上,伪造事件下发
        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            //[lxb #5] ListenerInfo 是一个listener集合, 包括onTouchListener、onClickListener等
            //[lxb #5] 我们调用setOnTouchListener 则是将listener 加入到 li.mOnTouchListener
            ListenerInfo li = mListenerInfo;

            //[lxb #6] 如果在View的情况下ENABLED,并且我们设置了OnTouchListener,事件将优先派发给我们设置的listener处理
            //[lxb #6] 如果OnTouchListener.onTouch消耗了该事件,那么将不会传递下去了
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            //[lxb #7] 将事件交给自身的onTouchEvent处理
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        //[lxb #8] 调试使用的,不需要理会
        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
        	//[lxb #9] 可以查看 [lxb #3]
            stopNestedScroll();
        }
        //[lxb #10] 将事件处理的结果反馈给父类,也就是自身是否要消耗改事件,如果在down的情况下
        //[lxb #10] 如果在Action_Down的情况下,返回了true就是告诉父类,我将需要接受接下来的事件
        //[lxb #10] 如果在之后的事件动作如ACTION_MOVE返回了false,事件依旧会派发下拉
        return result;
    }




新建一个类MyButton,该类继承自Button 然后重写其中的onTouchEvent和dispatchTouchEvent方法 ``` public class MyButton extends Button { private Context mContext; public MyButton(Context context) { super(context); this.mContext = context; } public MyButton(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:MOVE"); break; case MotionEvent.ACTION_UP: Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:UP"); break; } Log.d("TTTT", "========================== "); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("TTTT", "context:" + mContext + "MyButton|onTouchEvent|return:" + super.onTouchEvent(event) + "|event:DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d("TTTT", "context:" + mContext + "MyButton|onTouchEvent|return:" + super.onTouchEvent(event) + "|event:MOVE"); break; case MotionEvent.ACTION_UP: Log.d("TTTT", "context:" + mContext + "MyButton|onTouchEvent|return:" + super.onTouchEvent(event) + "|event:UP"); break; } return super.onTouchEvent(event); } } ``` 然后将这个Button加入到布局文件中,然后在Activity中给这个Button设置一个onTouchListener ``` <LinearLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.testtouchevent.MainActivity" > <com.example.testtouchevent.MyButton android:id="@+id/bt" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮" /> </LinearLayout> ``` ``` public class MainActivity extends Activity { private Button bt; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt = (Button) findViewById(R.id.bt); bt.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); } } ``` 按下按钮时候,log显式 ``` 03-31 11:51:08.493: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:DOWN 03-31 11:51:08.493: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|dispatchTouchEvent|return:true|event:DOWN 03-31 11:51:08.493: D/TTTT(1609): ========================== 03-31 11:51:08.493: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:DOWN ``` 松开按钮时候,log显式: ``` 03-31 11:51:10.285: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:UP 03-31 11:51:10.285: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|dispatchTouchEvent|return:true|event:UP 03-31 11:51:10.285: D/TTTT(1609): ========================== 03-31 11:51:10.289: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:UP ``` 可见onTouchEvent方法是在dispatchTouchEvent方法之前执行的 可是View的事件传递机制不是dispatchTouchEvent方法先执行用来传递消息的吗? 请问这是怎么回事儿?
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页