• 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设计思想》   林学森

  • 相关阅读:
    mybatis批量更新策略
    tk.mybatis扩展通用接口
    IDEA入门——jdbc连接和工具类的使用
    tensorflow——3
    再战tensorflow
    tensorflow初学
    Anaconda和TensorFlow安装遇到的坑记录
    《企业应用架构模式》——阅读笔记3
    机器学习十讲——第十讲
    机器学习十讲——第九讲
  • 原文地址:https://www.cnblogs.com/deman/p/5812570.html
Copyright © 2020-2023  润新知