zoukankan      html  css  js  c++  java
  • Android事件分发机制浅析(1)

    本文来自网易云社区

    作者:孙有军


    事件机制是Android中一个比较复杂且重要的知识点,比如你想自定义拦截事件,或者某系组件中嵌套了其他布局,往往会出现这样那样的事件冲突,坑爹啊!!事件主要涵盖onTouch,onClick,onTouchEvent,dispatchTouchEvent,onInterceptTouchEvent等等一系列事件,并且事件间还相互交互耦合,甚至有的事件还有返回值,一会true,一会false,什么情况下返回true,什么情况下返回false,为什么要有返回值,想想这些就感觉整个人都不好了。

    但是(万恶的但是),该知识点还是必须要掌握的,知识的深度与广度决定了你走的远度,鉴于此我们就来捅一捅该知识点。


    准备工作

    俗话说工欲善其事必先利其器,为了看他的执行流程,我们还是先写个样例,打几个日志看看执行流程吧!

    首先自定义一个外层布局的Layout,自定义Layout继承了LinearLayout,复写了相应的函数,在调用之前输入日志。如下:

    public class Layout extends LinearLayout {
        public Layout(Context context) {
            super(context);
            init();
        }
    
        public Layout(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public Layout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            //requestDisallowInterceptTouchEvent(false);
        }
    
    
        @TargetApi(Build.VERSION_CODES.KITKAT)
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.e("Event", "Layout onInterceptTouchEvent " + MotionEvent.actionToString(ev.getAction()));
            return super.onInterceptTouchEvent(ev);
        }
    
        @TargetApi(Build.VERSION_CODES.KITKAT)
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("Event", "Layout onTouchEvent " + MotionEvent.actionToString(event.getAction()));
            return super.onTouchEvent(event);
        }
    
        @TargetApi(Build.VERSION_CODES.KITKAT)
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            Log.e("Event", "Layout dispatchTouchEvent " + MotionEvent.actionToString(event.getAction()));
            return super.dispatchTouchEvent(event);
        }
    
    }

     我们还自定义了一个LogTextView,继承自TextView,也是为了输出日志,代码如下:

    public class LogTextView extends TextView {
    
        public LogTextView(Context context) {
            super(context);
        }
    
        public LogTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public LogTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(Build.VERSION_CODES.KITKAT)
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("Event", "TextView  onTouchEvent " + MotionEvent.actionToString(event.getAction()));
            return super.onTouchEvent(event);
        }
    
        @Override
        public void setOnTouchListener(OnTouchListener l) {
            super.setOnTouchListener(l);
        }
    
        @Override
        public void setOnClickListener(OnClickListener l) {
            super.setOnClickListener(l);
        }
    }

    接下来是布局文件了:

    <?xml version="1.0" encoding="utf-8"?>
    <com.sunny.event.wigdet.Layout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">
    
        <com.sunny.event.wigdet.LogTextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="40dp"
            android:background="#999999"
            android:padding="20dp"
            android:text="Hello World!"/>
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:src="@mipmap/ic_launcher"/>
    </com.sunny.event.wigdet.Layout>

           布局中嵌套了两个view,一个TextView,一个ImageView。最后就是主界面了。

    public class MainActivity extends AppCompatActivity {
    
        private LogTextView tv;
        private ImageView imageView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            findViews();
            setViewListener();
        }
    
        private void findViews() {
            tv = (LogTextView) findViewById(R.id.textView);
            imageView = (ImageView) findViewById(R.id.image);
        }
    
        private void setViewListener() {
            tv.setOnTouchListener(new View.OnTouchListener() {
                @TargetApi(Build.VERSION_CODES.KITKAT)
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.e("Event", "TextView  onTouch " + MotionEvent.actionToString(event.getAction()));
                    return true;
                }
            });
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("Event", "TextView  onClick ");
                }
            });
            imageView.setOnTouchListener(new View.OnTouchListener() {
                @TargetApi(Build.VERSION_CODES.KITKAT)
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.e("Event", "ImageView onTouch " + MotionEvent.actionToString(event.getAction()));
                    return false;
                }
            });
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("Event", "ImageView onClick ");
                }
            });
        }
    }

    执行结果

           round 1

           TextView的onTouch返回为false,点击TextView,日志如下:

    05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_DOWN
    05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_DOWN
    05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_DOWN
    05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: TextView  onTouchEvent ACTION_DOWN
    05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_MOVE
    05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_MOVE
    05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_MOVE
    05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: TextView  onTouchEvent ACTION_MOVE
    05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_UP
    05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_UP
    05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_UP
    05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView  onTouchEvent ACTION_UP
    05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView  onClick

    根据日志我们可以看到首先有一个ACTION_DOWN事件,执行的顺序是Layout的dispatchTouchEvent→onInterceptTouchEvent→(TextView)onTouch要→onTouchEvent,之后的我帕金森发生了,产生了ACTION_MOVE事件,传递的顺序与Down是一致的,最后一个事件是UP事件,正常点击不滑动是不会产生MOVE事件的,在这个这个三个事件最后调用了TextView的onClick事件。

    小结:

    1.  事件的传递顺序是先外层容器,之后再是具体的View。

    2. onTouch事件先于onTouchEvent事件,onTouchEvent先于onClick事件

       round 2

           我们将TextView的onTouch事件返回true。重新执行。执行顺序如下:

    05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_DOWN
    05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_DOWN
    05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_DOWN
    05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_MOVE
    05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_MOVE
    05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_MOVE
    05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_UP
    05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_UP
    05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: TextView  onTouch ACTION_UP

    从日志可以看出如果onTouch返回为true,执行顺序变成了如下:    

    首先还是ACTION_DOWN事件(Layout)dispatchTouchEvent→onInterceptTouchEvent→(TextView)onTouch,ACTION_MOVE与ACTION_UP执行顺序同ACTION_DOWN,可以发现的是TextView的onTouchEvent事件没有了,并且onClick事件也没有了。

    小结

    1、onTouch事件的返回值为true会拦截onTouchEvent事件

    2、onTouchEvent与onClick有关联

    上面的两次执行中每次都调用了onInterceptTouchEvent事件,这个到底又是啥?我们去看看他的返回值是什么?


    网易云免费体验馆,0成本体验20+款云产品! 

    更多网易研发、产品、运营经验分享请访问网易云社区


    相关文章:
    【推荐】 HTTP/2部署使用

  • 相关阅读:
    【转载】LTE中RB、RE、CP、REG、CCE、子载波
    LTE中,DCI和UCI为什么要定义那么多格式
    LTE中的PDCCH介绍
    ARQ
    (转)MYSQL远程登录权限设置
    (转)忘记wamp-mysql数据库root用户密码重置方法
    phpwind部署问题
    在aliyun遇到一些问题
    (转)PHP5使用cookie时报错 cannot modify header information
    (转)WAMP多站点配置
  • 原文地址:https://www.cnblogs.com/163yun/p/9705659.html
Copyright © 2011-2022 走看看