美文网首页
Touch事件的传递机制

Touch事件的传递机制

作者: 朝花夕拾不起来 | 来源:发表于2017-03-09 14:31 被阅读50次
    跟touch事件相关的3个方法:

    public boolean dispatchTouchEvent(MotionEvent ev); //用来分派event
    public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event
    public boolean onTouchEvent(MotionEvent ev); //用来处理event

    拥有这三个方法的类:
    Paste_Image.png
    三个方法的用法:
    Paste_Image.png

    首先,来看一个例子:这是一个基本的activity,在LinearLayout中放置了一个Button。我们将通过对Button等空间的触摸来分析android系统触摸事件处理机制。

    public class ListenerActivity extends Activity implements View.OnTouchListener, View.OnClickListener {
        private LinearLayout mLayout;
        private Button mButton;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.main);
    
            mLayout = (LinearLayout) this.findViewById(R.id.mylayout);
            mButton = (Button) this.findViewById(R.id.my_btn);
            //对layout和button分别注册触摸监听器
            mLayout.setOnTouchListener(this);
            mButton.setOnTouchListener(this);
    
            mLayout.setOnClickListener(this);
            mButton.setOnClickListener(this);
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.i(null, "OnTouchListener--onTouch-- action="+event.getAction()+" --"+v);
            return false;
        }
    
        @Override
        public void onClick(View v) {
            Log.i(null, "OnClickListener--onClick--"+v);
        }
    }
    

    ViewGroup是View的子类)布局,布局中包含一个按钮(View的子类);然后分别对这两个控件设置了Touch与Click的监听事件,具体运行结果如下:.

    1. 当稳稳的点击Button时打印如下:可以看见,触发了button注册的onTouch和onClick事件


      触摸Button时的日志
    2. 当稳稳的点击除过Button以外的其他地方时打印如下: 可以看见,触发了LinearLayout注册的onTouch和onClick事件


      触摸button之外地方的日志
    3. 当收指点击Button时按在Button上晃动了一下松开后的打印如下: 触发了button的多次onTouch事件
      Paste_Image.png

    我们看下onTouch和onClick,从参数都能看出来onTouch比onClick强大灵活,毕竟多了一个event参数。这样onTouch里就可以处理ACTION_DOWN、ACTION_UP、ACTION_MOVE等等的各种触摸。现在来分析下上面的打印结果;在1中,当我们点击Button时会先触发onTouch事件(之所以打印action为0,1各一次是因为按下抬起两个触摸动作被触发)然后才触发onClick事件;在2中也同理类似1;在3中会发现onTouch被多次调运后才调运onClick,是因为手指晃动了,所以触发了ACTION_DOWN->ACTION_MOVE…->ACTION_UP。

    如果你眼睛比较尖你会看见onTouch会有一个返回值,而且在上面返回了false。你可能会疑惑这个返回值有啥效果?那就验证一下吧,我们将上面的onTouch返回值改为ture。如下:

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.i(null, "OnTouchListener--onTouch-- action="+event.getAction()+" --"+v);
            return true;
        }
    
    如果onTouch返回true则onClick不会被调运了
    如果onTouch返回true则onClick不会被调运了。

    总结结论

    经过这个简单的实例验证你可以总结发现:

    • Android控件的Listener事件触发顺序是先触发onTouch,其次onClick。
    • 如果控件的onTouch返回true将会阻止事件继续传递,返回false事件会继续传递。

    从View的dispatchTouchEvent方法说起

    在Android中你只要触摸控件首先都会触发控件的dispatchTouchEvent方法(其实这个方法一般都没在具体的控件类中,而在他的父类View中),所以我们先来看下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)) {
                //noinspection SimplifiableIfStatement
                ListenerInfo li = mListenerInfo;
                if (li != null && li.mOnTouchListener != null
                        && (mViewFlags & ENABLED_MASK) == ENABLED
                        && li.mOnTouchListener.onTouch(this, event)) {
                    result = true;//如果view时enable的测onTouch返回true
                }
                //在这里调用onTouchEvent()
                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;
        }
    

    li.mOnTouchListener是不是null取决于控件(View)是否设置setOnTouchListener监听,在上面的实例中我们是设置过Button的setOnTouchListener方法的,所以也不为null;接着通过位与运算确定控件(View)是不是ENABLED 的,默认控件都是ENABLED 的;接着判断onTouch的返回值是不是true。通过如上判断之后如果都为true则设置默认为false的result为true,那么接下来的if (!result && onTouchEvent(event))就不会执行,最终dispatchTouchEvent也会返回true。而如果if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event))语句有一个为false则if (!result && onTouchEvent(event))就会执行,如果onTouchEvent(event)返回false则dispatchTouchEvent返回false,否则返回true。

    这下再看前面的实例部分明白了吧?控件触摸就会调运dispatchTouchEvent方法,而在dispatchTouchEvent中先执行的是onTouch方法,所以验证了实例结论总结中的onTouch优先于onClick执行道理。如果控件是ENABLE且在onTouch方法里返回了true则dispatchTouchEvent方法也返回true,不会再继续往下执行;反之,onTouch返回false则会继续向下执行onTouchEvent方法,且dispatchTouchEvent的返回值与onTouchEvent返回值相同。

    所以依据这个结论和上面实例打印结果你指定已经大胆猜测认为onClick一定与onTouchEvent有关系?是不是呢?先告诉你,是的。下面我们会分析。

    相关文章

      网友评论

          本文标题:Touch事件的传递机制

          本文链接:https://www.haomeiwen.com/subject/vvhwgttx.html