li_zyx
2016-03-31 16:05
采纳率: 100%
浏览 3.2k

为什么onTouchEvent方法会在dispatchTouchEvent方法之前执行呢?

新建一个类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方法先执行用来传递消息的吗?
请问这是怎么回事儿?

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

4条回答 默认 最新

  • szw_sh 2016-04-01 05:34

    应该是button是最后一层,没有子类了,事件传递是向子类传递,如果没有子类就会调用onTouchEvent方法

    打赏 评论
  • HuangShuiXiang 2016-04-01 07:25

    首先,button的enabled默认为true,那么在没有OnTouchListener或者有OnTouchListener但是返回false的时候,会第一时间传递给onTouchEvent,
    第二,当前Button是最后一级,没有子类可派发事件

    打赏 评论
  • 本人很懒zzz 2021-03-05 14:36
     public class MyButton extends Button {
     
        。。。。。省略代码。。。。。。
     
        @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);
        }
     
       。。。。。省略代码。。。。。。
    }

    onTouchEvent方法会在dispatchTouchEvent方法之前执行是因为你在打印日志的时候调用了super.super.dispatchTouchEvent(event)

    即在DOWN和UP打印日志的代码:

     Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:DOWN");
     Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:UP");

    这时候会调用MyButton的父类View的dispatchTouchEvent方法

     public boolean dispatchTouchEvent(MotionEvent event) {
            // If the event should be handled by accessibility focus first.
            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;
    
            if (mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onTouchEvent(event, 0);
            }
    
            final int actionMasked = event.getActionMasked();
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Defensive cleanup for new gesture
                stopNestedScroll();
            }
    
            if (onFilterTouchEventForSecurity(event)) {
                if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                    result = true;
                }
                //noinspection SimplifiableIfStatement
                ListenerInfo li = mListenerInfo;
                if (li != null && li.mOnTouchListener != null
                        && (mViewFlags & ENABLED_MASK) == ENABLED
                        && li.mOnTouchListener.onTouch(this, event)) {
                    result = true;
                }
    
                if (!result && onTouchEvent(event)) {
                    result = true;
                }
            }
    
            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)) {
                stopNestedScroll();
            }
    
            return result;
        }
    

     这个时候就调用了MyButton的onTouchEvent方法会先打印 onTouchEvent的日志 

    然后再打印 dispatchTouchEvent 日志,在MyButton#dispatchTouchEvent 最后又return super.dispatchTouchEvent(event);

    这时又会调用MyButton的onTouchEvent再打印一次 onTouchEvent的日志 

    最终得日志就变成了

     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
     

    更详细内容我写了一篇文章:https://blog.csdn.net/u011288271/article/details/114395637

    打赏 评论
  • 本人很懒zzz 2021-03-05 14:46
     public class MyButton extends Button {
     
        。。。。。省略代码。。。。。。
     
        @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);
        }
     
       。。。。。省略代码。。。。。。
    }

    onTouchEvent方法会在dispatchTouchEvent方法之前执行是因为你在打印日志的时候调用了super.super.dispatchTouchEvent(event)

    即在DOWN和UP打印日志的代码:

     Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:DOWN");
     Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:UP");

    这时候会调用MyButton的父类View的dispatchTouchEvent方法

     public boolean dispatchTouchEvent(MotionEvent event) {
            // If the event should be handled by accessibility focus first.
            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;
    
            if (mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onTouchEvent(event, 0);
            }
    
            final int actionMasked = event.getActionMasked();
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Defensive cleanup for new gesture
                stopNestedScroll();
            }
    
            if (onFilterTouchEventForSecurity(event)) {
                if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                    result = true;
                }
                //noinspection SimplifiableIfStatement
                ListenerInfo li = mListenerInfo;
                if (li != null && li.mOnTouchListener != null
                        && (mViewFlags & ENABLED_MASK) == ENABLED
                        && li.mOnTouchListener.onTouch(this, event)) {
                    result = true;
                }
    
                if (!result && onTouchEvent(event)) {
                    result = true;
                }
            }
    
            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)) {
                stopNestedScroll();
            }
    
            return result;
        }
    

     这个时候就调用了MyButton的onTouchEvent方法会先打印 onTouchEvent的日志 

    然后再打印 dispatchTouchEvent 日志,在MyButton#dispatchTouchEvent 最后又return super.dispatchTouchEvent(event);

    打印你添加的分割线: Log.d("TTTT", "========================== ");

    这时又会调用MyButton的onTouchEvent再打印一次 onTouchEvent的日志 

    最终得日志就变成了

     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
     

    更详细内容我写了一篇文章:https://blog.csdn.net/u011288271/article/details/114395637

    打赏 评论

相关推荐 更多相似问题