美文网首页
Android事件分发实践

Android事件分发实践

作者: boboyuwu | 来源:发表于2017-12-20 15:46 被阅读15次

    ViewGroup->View 事件分发源码已经分析完毕了,全文分析源码未免太枯燥了,下面我们敲个案例测试一下也是对前面源码分析进行一下验证

    public class TestView extends View {
        public TestView(Context context) {
            this(context, null);
        }
    
        public TestView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            //设置onClick点击监听事件
            setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("wwwonClick", "onClick");
                }
            });
            //设置onTouch触摸监听
            setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    boolean flag=false;
                    switch (event.getAction()){
                        case  MotionEvent.ACTION_DOWN:
                            flag=false;
                            Log.e("wwwonTouch","ACTION_DOWN");
                            break;
                        case MotionEvent.ACTION_MOVE:
                            flag=false;
                            Log.e("wwwonTouch","ACTION_MOVE");
                            break;
    
                        case MotionEvent.ACTION_UP:
                            flag=false;
                            Log.e("wwwonTouch","ACTION_UP");
                            break;
                    }
                    return false;
                }
            });
        }
      }
    

    我们简单设置一个简单的View控件,控件中设置了一个点击事件,设置了一个onTouch触摸监听,在layout布局中加入这个控件并把控件的背景色设置为"#F00"红色,我们手指点击->滑动一段距离->再松开后看下打印结果


    ScreenGif111.gif

    可以看到onTouch中DOWN事件被执行伴随着一系列的MOVE事件最后是UP事件然后执行了我们的onClick,如果你看过我的这篇关于事件分发的源码分析:http://www.jianshu.com/p/9aa26face07e 你会很清楚的明白这个打印现象正是我们源码中分析出来的结果,如果你没有认真看或者没有弄明白你可能会被这个问题弄得一头雾水,咦?onTouch中DOWN事件明明返回了false,为什么后面的MOVE->UP事件还照常执行呢???

    在上面链接文章中,我们分析出View是有onTouchEvent()默认实现的,当我们onTouch方法中case DOWN执行后并返回false,那么View就会把这个DOWN事件分发给onTouchEvent()方法,在这个方法中我们也分析了所有事件默认都返回true,默认全部都消费,那么View的dispatchTouchEvent方法也会返回true,所以ViewGroup会认为child要消费这个Down事件,而且我们源码中也可以看出ViewGroup只是根据一开始的Down事件判断child愿不愿意消费这个事件只要child消费了down事件那么这个序列后序的move->up等事件都会交给child处理(所以如果没设置事件拦截的情况下,只要Down事件被消费了,那么Down后面的事件返回true或者false都是无效的,事件都会交给child处理),所以onTouchEvent()方法默认处理down事件时返回了true,后面事件来临时,onTouch的move->up事件都会响应,现在清楚为什么了吧?

    我们改下代码验证看下我说的对不对

    public class TestView extends View {
        public TestView(Context context) {
            this(context, null);
        }
    
        public TestView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("wwwonClick", "onClick");
                }
            });
            setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    boolean flag = false;
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            flag = false;
                            Log.e("wwwonTouch", "ACTION_DOWN");
                            break;
                        case MotionEvent.ACTION_MOVE:
                            flag = false;
                            Log.e("wwwonTouch", "ACTION_MOVE");
                            break;
    
                        case MotionEvent.ACTION_UP:
                            flag = false;
                            Log.e("wwwonTouch", "ACTION_UP");
                            break;
                    }
                    return false;
                }
            });
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean flag = false;
            super.onTouchEvent(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    flag = false;
                    Log.e("wwwonTouchEvent", "ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    flag = true;
                    Log.e("wwwonTouchEvent", "ACTION_MOVE");
                    break;
    
                case MotionEvent.ACTION_UP:
                    flag = true;
                    Log.e("wwwonTouchEvent", "ACTION_UP");
                    break;
            }
            return flag;
        }
    }
    

    我们重写了onTouchEvent方法并在DOWN事件开始就返回false不去消费down事件我们打印下结果看下

    ScreenGif2.gif

    可以看到无论我们怎么滑动,在onTouch和onTouchEvent方法中只有Down事件被打印(down事件是没法屏蔽的)parent认为child不消费这个事件序列,然后自己去处理这个事件序列了,我们在Down事件中返回true那么结果又会跟最开始那种结果一样了。

    这里需要注意一点,我们重写onTouchEvent时需要带上super.onTouchEvent()否则是接收不到onClick事件的。

    下面再敲个案例验证一下child不消费down事件后抛给parent处理

    public class TestViewGroup extends FrameLayout{
        public TestViewGroup(@NonNull Context context) {
            this(context,null);
        }
    
        public TestViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public TestViewGroup(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            boolean flag = false;
            super.onTouchEvent(event);
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    flag = true;
                    Log.e("wwwGrouponTouchEvent", "ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    flag = false;
                    Log.e("wwwGrouponTouchEvent", "ACTION_MOVE");
                    break;
    
                case MotionEvent.ACTION_UP:
                    flag = false;
                    Log.e("wwwGrouponTouchEvent", "ACTION_UP");
                    break;
            }
            return flag;
        }
    

    我们创建一个TestViewGroup简单继承与FrameLayout然后设置到上面TestView 的layout布局中,然后我让TestView 中onTouch方法和onTouchEvent方法中的down事件全部返回false,此时我们滑动一下看下打印结果

    ScreenGif4.gif

    可以看到结果十分清晰,child不消费down事件后抛给parent,parent在Down事件中返回true,表示自己消费这次事件序列,如果你仔细观察可以发现我再MOVE以及UP事件中都故意返回了false,这也是为了验证我上面说的只要Down事件被消费其他的事件就算返回false也是自己处理不会再向上抛了。

    相关文章

      网友评论

          本文标题:Android事件分发实践

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