关闭

android的事件传递机制

59人阅读 评论(0) 收藏 举报

参考http://www.jianshu.com/p/2be492c1df96

  • onInterceptTouchEvent:

    • 事件拦截方法,只有在ViewGrop中存在,因为事件都会先传递到最顶层的parent,如果它没有做处理,该事件(dowm,up等事件)就会传递到下一层,下一层可能是view,也可能是viewgrop,如果是view则会进入它的onTouchEvent,如果是viewGroup则会进入它的onInterceptTouchEvent,以这样的规则往下传递。
    • 而且这个事件只能是一个一个处理:例如如果拦截了dowm事件,后面的move和up事件不会一起被拦截(就算底层的view处理了dowm事件,接下来的move事件还会传递到最顶层viewGroup的onInterceptTouchEvent中),这点与onTouchEvent不同!
  • onTouchEvent:

    • 只存在view(图中的C)中,一旦拦截了dowm事件,那么后面的move和up事件将不会传递到parent(图中的A和B)的onTouchEvent中!但是:后面的事件还是会先传递到A和B的onInterceptTouchEvent方法中的,如果此时A在它的onInterceptTouchEvent中对move事件进行了拦截,则C就不会再收到move事件(比如scroll事件就需要这样做,当滑动超出一定距离后就认为是滑动,需要拦截)。所以,在编码中,我们也常常在view的dispatchTouchEvent方法中通过getParent().requestDisallowInterceptTouchEvent(true);来确保父控件无法拦截对应的事件(从博文中看出一定是成功的),如下:
    getParent().requestDisallowInterceptTouchEvent(true);
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if (如果父容器需要这个事件) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }//否则的话 就交给自己本身view的onTouchEvent自动处理了
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(event);
    }
    • (从博文中看出一定是成功的,所以这里应该没必要了)除了上面一个步骤,还需要确认父控件的onInterceptTouchEvent没有对事件进行拦截(父控件未拦截则不影响),否则子控件将无法收到该事件,如下:
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            return false;
        }
        return true;
    }
    • 如果最底层的view没有设置成clickable或者拦截这个事件,那么该事件将会继续往上传递到parent的onTouchEvent方法中。
  • 如图,事件的处理实际上经历了一下一上两个过程,下是指A->B的onInterceptTouchEvent,上是指C->B->A的onTouchEvent,当然,任意一步的方法中返回true,都能阻止它继续传播

  • 总结

    • 事件最先由ViewGroup的onInterceptTouchEvent往下一层一层传递到View的onTouchEvent,如果没有拦截,则再会往上一层一层传递到ViewGroup的onTouchEvent。所以可理解为onInterceptTouchEvent自上而下传递,onTouchEvent自下而上传递。
    • 一般如果某个View在onTouchEvent中拦截了dowm事件,如果没有必要,他的parent(ViewGroup)不会拦截接下来的事件。但是也有例外,这时候就会出现事件拦截(冲突),如子控件在onTouchEvent的dowm中返回了true,后面它想在move和up事件中也做一些处理,假设接下来它的parent在move事件中判断滑动大于某个值,它需要处理,并在onInterceptTouchEven的move中返回了true,此时这个事件将会变成一个CANCLE事件(文章中讲到的),view无法再处理了,而最后会传递到ViewGrop的onTouchEvent的move事件中。
    • 虽然上面ViewGroup可以中途拦截View的事件,但是在一些必要的情况下我们可以通过getParent().requestDisallowInterceptTouchEvent(true);来确保父控件无法拦截对应的事件。(这个是否一定成功吗?)
    • 其实dispatchTouchEvent才是整个事件处理的真正入口。如果我们需要监听整个Activity的事件并作出对应的处理(如监听点击非输入框的时候隐藏输入法),这样我们就可以重写Activity的事件分发方法dispatchTouchEvent,并判断当前获取焦点的view是否是EditText,不是则隐藏输入法。
    • 注意与前面的两个方法不同,dispatchTouchEvent返回false则表示拦截!!!
  • 事件传递的基本流程:

    • 事件都是从Activity.dispatchTouchEvent()开始传递;
    • 事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递;
    • 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数;
    • 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来;
    • OnTouchListener优先于onTouchEvent()对事件进行消费。也就是如果View设置了onTouchListener,会先执行OnTouchListener的onTouch方法。如果该方法返回true,表示消费了。不会执行onTouchEvent了,如果onTouch返回false,就会继续执行onTouchEvent
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:6820次
    • 积分:408
    • 等级:
    • 排名:千里之外
    • 原创:36篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1条
    文章分类
    文章存档
    最新评论