美文网首页
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