Android常考问题(1) - 触摸事件

12 篇文章 0 订阅
10 篇文章 0 订阅

Android的触摸事件分成了三个步骤:从按下去(ACTION_DOWN)到移动(ACTION_MOVE)最后离开屏幕(ACTION_UP),这三个就是三种触摸事件,属于一个名叫MotionEvent的类。

Android的触摸机制也有三个阶段,我们用一整个触摸事件的发生过程来说明一下。

发生触摸事件后,一般由Activity发起dispatchTouchEvent这个方法,这个方法过程叫分发(Dispatch),也就是第一个步骤,这个方法如果返回值为true,这个触摸事件就直接在这里直接被解决。

Activity处理触摸事件的代码如下,在发生触摸事件之后通过getWindow().superDispatchTouchEvent(ev)方法继续把触摸事件传递给包含的View,如果view处理掉事件了,那就直接return true,也就是上面说的触摸事件被解决了。如果没有被处理,那就会把事件传到自己的onTouchEvent方法里面处理。但是即使activity里面的onTouch方法没有解决事件,事件也算结束,因为不会再传递了。

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

当事件从Activity传出之后,我们知道安卓有很多的View组成的ViewGroup。事件会被传到最外层的ViewGroup。这时候ViewGroup的分发方法,也就是dispatchTouchEvent,都会启动事件的分发过程。但是在ViewGroup的分发之前,都会先判断该ViewGroup是否拦截(Intercept),这就是第二个阶段,拦截过程。也就是调用onInterceptTouchEvent方法,当这个方法返回true的时候,这个ViewGroup就接下了这个事件,就由他干活儿了,当不去拦截事件的话,ViewGroup就会再次把事件丢到下一层的ViewGroup里面。直到最内层的ViewGroup,最内层的ViewGroup也会进行拦截,拦截了的话就由他解决事件,否则的话,就丢到底层的各个View,由最后的View来解决。

在ViewGroup里面,谁拦截了事件,谁就来处理事件,而不管Activity,ViewGroup,View之中,最后到了谁身上,处理事件的方式都是最后的消费(Consume),也就是调用onTouchEvent方法进行消费。

接着进行事件的传递,事件给了view,view就会调用自身的分发方法dispatchTouchEvent,在这个方法里面view回去干活,调用自己的onTouchEvent方法,如果返回了true,那就解决了事件。可是如果view没有写onTouchEvent,就会返回false,那就说明它解决不掉这个事件,然后它就又把问题丢给上一层的ViewGroup,直接调用上一层的onTouchEvent处理,上一层处理了就行,处理不了就一直往上传,最后又回到了上面的Activity里面返回了false,也就是调用了Activity的onTouchEvent,不管处理成功没,反正事事件到这里就结束了。这就是整个流程,具体流程图如下:

需要注意的是每次ViewGroup得到事件以后,都会先问一遍自己里面的View有没有正好在被点击的地方,如果有就交给他处理了,没有的话才会向下传递。

 

这里说一个例子。我之前的项目中在整个页面使用了ViewGroup布局,实现滑动切换页面,而在页面中还加入了轮播图的第二个ViewGroup。当时只写了onTouchEvent方法,这时候会出现有时候滑动的时候外层会不响应,因为滑动事件被内部的轮播图抢走了,轮播图自己换了可是页面没有换。这里其实就需要触摸事件拦截,需要在外层拦截到属于自己的滑动事件,就需要重写外层的拦截方法。而内部轮播图,又有滑动和点击两种事件,这种时候需要用上另一个onTouch方法,onTouch方法是另一个触摸事件方法,onTouch里面也是事件处理过程,但是onTouch的优先级高于onTouchEvent。如果onTouch()方法返回值是true(事件被解决)时,则onTouchEvent()方法将不会被执行,因此可以把ACTION_MOVE事件放入onTouch里面,这样的话如果滑动了就会把触摸事件直接解决,不会再传递到onTouchEvent里面进行点击事件的处理(onClick在onTouchEvent方法中执行)。因为View的onClick方法是在ACTION_UP之后执行,所以如果上层ViewGroup中拦截了ACTION_MOVE或者ACTION_UP事件就不会执行了。在比如滑动拖动按钮的时候可以使用。

当然如果调用getParent().requestDisallowInterceptTouchEvent(true)方法请求父View不要拦截Touch事件。注意这个方法不能在子View初始化时调用(无效),最好在子View接收到Touch事件也就是在子View的dispatchTouchEvent方法中调用。调用完该方法后,父View以及父View的父View就不会再调用onInterceptTouchEvent方法去判断是否拦截了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值