美文网首页
View 的事件分发

View 的事件分发

作者: 再见亦是泪丶丶 | 来源:发表于2017-04-05 11:09 被阅读0次

    View的事件分发牵涉到以下三个方法:

    /*用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。*/
    dispatchTouchEvent(MotionEvent event);
    /*是dispatchTouchEvent的内部方法,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件*/
    onInterceptTouchEvent(MotionEvent event);
    /*在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件*/
    onTouchEvent(MotionEvent event);
    
    //上述方法的区别,可以用如下伪代码来表示:
    public boolean 
    

    根据一个Button的点击事件来看下事件的分发,代码如下:
    一个继承Button的自定义Button,并写好相应事件的log

    public class MyButton extends Button {
        private static final String TAG = MyButton.class.getSimpleName();
    
        public MyButton(Context context,AttributeSet attrs) {
            super(context,attrs);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "onTouchEvent: ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "onTouchEvent: ACTION_UP");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "onTouchEvent: ACTION_MOVE");
                    break;
            }
            return super.onTouchEvent(event);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "dispatchTouchEvent: ACTION_DOWN" );
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e(TAG, "dispatchTouchEvent: ACTION_UP" );
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e(TAG, "dispatchTouchEvent: ACTION_MOVE" );
                    break;
            }
            return super.dispatchTouchEvent(event);
        }
    
    
    }
    

    xml中:

    <?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:orientation="vertical">
    
        <com.sunfuyi.studytest.view.MyButton
            android:id="@+id/button1"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="button"></com.sunfuyi.studytest.view.MyButton>
    </LinearLayout>
    

    Activity中:

    public class TestViewActivity extends Activity {
        private static final String TAG = "MyButton";
        TestView tv;
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_testview);
    
            MyButton button = (MyButton) findViewById(R.id.button1);
            button.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    int action = event.getAction();
                    switch (action)
                    {
                        case MotionEvent.ACTION_DOWN:
                            Log.e(TAG, "onTouch ACTION_DOWN");
                            break;
                        case MotionEvent.ACTION_MOVE:
                            Log.e(TAG, "onTouch ACTION_MOVE");
                            break;
                        case MotionEvent.ACTION_UP:
                            Log.e(TAG, "onTouch ACTION_UP");
                            break;
                        default:
                            break;
                    }
    
                    return false;
                }
            });
        }
    }
    

    我们点击button,并查看log,得到结果如下:
    执行顺序
    首先:<b>dispatchTouchEvent</b>
    其次:<b>setOnTouchListener的onTouch回调</b>
    再次:<b>onTouchEvent</b>

    04-05 02:49:52.168 6673-6673/com.sunfuyi.studytest E/MyButton: dispatchTouchEvent: ACTION_DOWN
    04-05 02:49:52.168 6673-6673/com.sunfuyi.studytest E/MyButton: onTouch ACTION_DOWN
    04-05 02:49:52.168 6673-6673/com.sunfuyi.studytest E/MyButton: onTouchEvent: ACTION_DOWN
    04-05 02:49:52.254 6673-6673/com.sunfuyi.studytest E/MyButton: dispatchTouchEvent: ACTION_UP
    04-05 02:49:52.254 6673-6673/com.sunfuyi.studytest E/MyButton: onTouch ACTION_UP
    04-05 02:49:52.254 6673-6673/com.sunfuyi.studytest E/MyButton: onTouchEvent: ACTION_UP
    

    下面我们来看看源码:

     /**
         * Pass the touch screen motion event down to the target view, or this
         * view if it is the target.
         *
         * @param event The motion event to be dispatched.
         * @return True if the event was handled by the view, false otherwise.
         */
        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;
                }
    
                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;
        }
    

    以上代码有点长,我们直接过滤掉无关代码,直接看我们需要的:

     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;
                }
    
                if (!result && onTouchEvent(event)) {
                    result = true;
                }
            }
    

    以上代码我们来简单分析下,

    首先查看 onFilterTouchEventForSecurity(event)方法:

    //从源码注释我们可以看出该方法是 (过滤触摸事件的安全策略)
    //参数  MotionEvent,需要过滤的事件
    //return   如果事件应该被分发,则为真,如果事件应该被丢弃,则为假。
    /**
         * Filter the touch event to apply security policies.
         *
         * @param event The motion event to be filtered.
         * @return True if the event should be dispatched, false if the event should be dropped.
         *
         * @see #getFilterTouchesWhenObscured
         */
        public boolean onFilterTouchEventForSecurity(MotionEvent event) {
            //noinspection RedundantIfStatement
            if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                    && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
                // Window is obscured, drop this touch.
                return false;
            }
            return true;
        }
    
    
    

    这里默认返回 true,直接进入该if,我们看到。
    首先判断mOnTouchListener不为null,并且view是enable的状态,然后 mOnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ;如果onTouch返回的是false,那么第一个if不成立,就会走第二个if;就会调用onTouchEvent(event)方法。

    相关文章

      网友评论

          本文标题:View 的事件分发

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