zoukankan      html  css  js  c++  java
  • android 事件分发机制

    1.View的事件分发机制

    一个button,简单一点就是onTouch,还有onclick事件,我们一个一个来分析

    首先响应的是dispatchTouchEvent

    public boolean dispatchTouchEvent(MotionEvent event) {  
        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
                mOnTouchListener.onTouch(this, event)) {  
            return true;  
        }  
        return onTouchEvent(event);  
    }

    其实,在android源码的命名还是很有规律的,dispatchXXX,也就是分发机制,往往就是第一个需要响应的地方。

    我们来分析下:touchlistener不为空,也就是view的使用者设置了回调。

    第二个条件就是View必须是enable的。第三:onTouch返回false,就说明onTouch不消费该事件,由OnTouchEvent响应。

    如果返回True,那么就会直接return。

    所以onClick事件一定会被调到。

    public boolean onTouchEvent(MotionEvent event) {  
        final int viewFlags = mViewFlags;  
        if ((viewFlags & ENABLED_MASK) == DISABLED) {  
            // A disabled view that is clickable still consumes the touch  
            // events, it just doesn't respond to them.  
            return (((viewFlags & CLICKABLE) == CLICKABLE ||  
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
        }  
        if (mTouchDelegate != null) {  
            if (mTouchDelegate.onTouchEvent(event)) {  
                return true;  
            }  
        }  
        if (((viewFlags & CLICKABLE) == CLICKABLE ||  
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
            switch (event.getAction()) {  
                case MotionEvent.ACTION_UP:  
                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  
                    if ((mPrivateFlags & PRESSED) != 0 || prepressed) {  
                        // take focus if we don't have it already and we should in  
                        // touch mode.  
                        boolean focusTaken = false;  
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {  
                            focusTaken = requestFocus();  
                        }  
                        if (!mHasPerformedLongPress) {  
                            // This is a tap, so remove the longpress check  
                            removeLongPressCallback();  
                            // Only perform take click actions if we were in the pressed state  
                            if (!focusTaken) {  
                                // Use a Runnable and post this rather than calling  
                                // performClick directly. This lets other visual state  
                                // of the view update before click actions start.  
                                if (mPerformClick == null) {  
                                    mPerformClick = new PerformClick();  
                                }  
                                if (!post(mPerformClick)) {  
                                    performClick();  
                                }  
                            }  
                        }  
                        if (mUnsetPressedState == null) {  
                            mUnsetPressedState = new UnsetPressedState();  
                        }  
                        if (prepressed) {  
                            mPrivateFlags |= PRESSED;  
                            refreshDrawableState();  
                            postDelayed(mUnsetPressedState,  
                                    ViewConfiguration.getPressedStateDuration());  
                        } else if (!post(mUnsetPressedState)) {  
                            // If the post failed, unpress right now  
                            mUnsetPressedState.run();  
                        }  
                        removeTapCallback();  
                    }  
                    break;  
                case MotionEvent.ACTION_DOWN:  
                    if (mPendingCheckForTap == null) {  
                        mPendingCheckForTap = new CheckForTap();  
                    }  
                    mPrivateFlags |= PREPRESSED;  
                    mHasPerformedLongPress = false;  
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  
                    break;  
                case MotionEvent.ACTION_CANCEL:  
                    mPrivateFlags &= ~PRESSED;  
                    refreshDrawableState();  
                    removeTapCallback();  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    final int x = (int) event.getX();  
                    final int y = (int) event.getY();  
                    // Be lenient about moving outside of buttons  
                    int slop = mTouchSlop;  
                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||  
                            (y < 0 - slop) || (y >= getHeight() + slop)) {  
                        // Outside button  
                        removeTapCallback();  
                        if ((mPrivateFlags & PRESSED) != 0) {  
                            // Remove any future long press/tap checks  
                            removeLongPressCallback();  
                            // Need to switch from pressed to not pressed  
                            mPrivateFlags &= ~PRESSED;  
                            refreshDrawableState();  
                        }  
                    }  
                    break;  
            }  
            return true;  
        }  
        return false;  
    }  
    onTouchEvent

    最终会走到performClick这个方法。

        public boolean performClick() {
            final boolean result;
            final ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnClickListener != null) {
                playSoundEffect(SoundEffectConstants.CLICK);
                li.mOnClickListener.onClick(this);
                result = true;
            } else {
                result = false;
            }
    
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
            return result;
        }

    可以看到,如果setOnClickListener, onClick 就会走到。

    2.ViewGroup的事件分发机制

    <com.joyfulmath.frameworksample.viewdemo.MyLayout
            android:id="@+id/my_layout"
            android:background="#99000044"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <Button
                android:id="@+id/button_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="button"/>
            <Button
                android:id="@+id/imageId"
                android:layout_centerInParent="true"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@android:drawable/ic_lock_power_off"/>
        </com.joyfulmath.frameworksample.viewdemo.MyLayout>

    一个layout里面有2个button,

    package com.joyfulmath.frameworksample.viewdemo;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    
    import com.joyfulmath.frameworksample.R;
    
    /**
     * Created by Administrator on 2016/8/27 0027.
     */
    public class TestViewAction extends Activity implements View.OnClickListener,View.OnTouchListener
    {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.content_main);
            Button button = (Button) findViewById(R.id.button_id);
            button.setOnClickListener(this);
            button.setOnTouchListener(this);
            Button imageView = (Button) findViewById(R.id.imageId);
            imageView.setOnClickListener(this);
            imageView.setOnTouchListener(this);
            MyLayout myLayout = (MyLayout) findViewById(R.id.my_layout);
            myLayout.setOnTouchListener(this);
            myLayout.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId())
            {
                case R.id.button_id:
                    TraceLog.i("button_id");
                    break;
                case R.id.imageId:
                    TraceLog.i("imageId");
                    break;
                case R.id.my_layout:
                    TraceLog.i("my_layout");
                    break;
            }
    
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (v.getId())
            {
                case R.id.button_id:
                    TraceLog.i("button_id");
                    break;
                case R.id.imageId:
                    TraceLog.i("imageId");
                    break;
                case R.id.my_layout:
                    TraceLog.i("my_layout");
                    break;
            }
            return false;
        }
    }
    TestViewAction

    分别点击button1 & button2 & 灰色部分

    等到log如下:

    08-27 10:19:26.799 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
    08-27 10:19:26.880 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
    08-27 10:19:26.896 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
    08-27 10:19:26.913 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
    08-27 10:19:26.926 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
    08-27 10:19:26.926 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: button_id [at (TestViewAction.java:38)]
    08-27 10:19:27.434 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)]
    08-27 10:19:27.535 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)]
    08-27 10:19:27.543 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)]
    08-27 10:19:27.544 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: imageId [at (TestViewAction.java:41)]
    08-27 10:19:28.111 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
    08-27 10:19:28.156 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
    08-27 10:19:28.173 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
    08-27 10:19:28.190 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
    08-27 10:19:28.237 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
    08-27 10:19:28.237 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: my_layout [at (TestViewAction.java:44)]

    也就是点击button1以后,不会传递都layout

    But,如果layout里面有一个函数

    public boolean onInterceptTouchEvent(MotionEvent ev)

    这个函数就是截断对button的分发处理,默认是return false。

    至此,我们有了一个大概的流程。

    Activtiy->ViewGroup->View 

    如果仔细分析就会发现,在Activity里面有一个getDocView。所以Activity里面有个RootView的概念。

    言归正传,ViewGroup本质上也是一个View,所以,可以把模型简单的定性为Activtiy->ViewGroup->View 三层。

    首先Activity里面有2个函数,我们分析看看:

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            TraceLog.i();
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            TraceLog.i();
            return super.onTouchEvent(event);
        }

    所以大体流程如下:

    1.@Activty.diapatchTouchEvent ->@Layout.dispatchTouchEvent->@layout.onInterceptTouchEvent return true/false

    2.return true->@layout.onTouchEvent 后面部分同view

    3.return false->@view.dispatchTouchEvent View的分发见上一片流程。

    上面是2016年的文章分析,最新的java层的分析可以参考如下文章

    demanmath.com

    关注公共号:

    公众号

    参考:

    《深入理解android设计思想》   林学森

  • 相关阅读:
    LeetCode 88. Merge Sorted Array
    LeetCode 75. Sort Colors
    LeetCode 581. Shortest Unsorted Continuous Subarray
    LeetCode 20. Valid Parentheses
    LeetCode 53. Maximum Subarray
    LeetCode 461. Hamming Distance
    LeetCode 448. Find All Numbers Disappeared in an Array
    LeetCode 976. Largest Perimeter Triangle
    LeetCode 1295. Find Numbers with Even Number of Digits
    如何自学并且系统学习计算机网络?(知乎问答)
  • 原文地址:https://www.cnblogs.com/deman/p/5812570.html
Copyright © 2011-2022 走看看