Android View的事件

一、View 的基础知识

x = left + translationX; y = top + translationY

x 和 y 是View 左上角的坐标。

translationX和translationY是View左上角相对于父容器的偏移。

速度 = (终点位置- 起点位置)/ 时间。

从左向右滑动的话,速度为正。

二、View 的滑动

使用scrollTo/scrollBy实现。

scrollTo/scrollBy只能改变View内容的位置而不能改变View在布局中的位置。

当View左边缘在View内容左边缘的右边时,mScrollX为正,反之为负;当View上边缘在View内容上边缘时,mScrollY为正,反之为负。

scrollTo/scrollBy时,View的位置始终不变。都是View的内容在偏移。

换句话说,如果从左到右滑动,那么mScrollX为负值,反之为正。

计算mScrollX和mScrollY时,都是看View左边缘在View内容左边缘的右边时,为正。

scrollTo/scrollBy时,View的位置始终不变。都是View的内容在偏移。所以最后是看原始内容位置(也就是View左边缘)- 现在位置(View内容作边缘)。 刚好与上面的速度相反。

从左向右滑动的话,速度为正。

如果从左到右滑动,那么mScrollX为负值,反之为正。

scrollTo/scrollBy:操作简单,适合对View内容的滑动。

动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果。

改变布局参数:操作稍微复杂,适用于有交互的View。

三、View的弹性滑动

View的弹性滑动实现方式:

1.使用Scroller;

这里的滑动也是指View内容的滑动而非View本身位置的改变。

当View重绘后会在draw方法中调用computeScroll,而computeScroll又会去向Scroller获得当前的scrollX和scrollY;然后通过scrollTo方法实现滑动;接着又调用postInvalidate方法来进行第二次重绘。一直下去,直到整个滑动过程结束。View的每一次重绘都会导致View进行小幅度的滑动,而多次小幅度滑动就组成了弹性滑动。

2.使用动画;

我们在动画的每一帧到来时获取动画完成的比例,然后再根据这个比例计算出当前View所要滑动的距离。这里的滑动也是针对View的内容而非View的本身。

都是通过改变一个百分比配合scrollTo方法来完成View的滑动。采用这种方法除了能够完成弹性滑动以外,还可以实现其他动画效果,我们完全可以在onAnimationUpdate方法中加上我们想要的其他操作。

3.使用延时策略。

通过发送一系列延时消息从而达到一种渐近式的效果,具体可以使用Handler或者View的postDelayed方法,也可以使用线程的sleep。

对于postDelayed方法来说,我们可以通过它来延时发送一个消息,然后在消息中来进行View的滑动,如果接连不断地发送这种延时消息,那么就可以实现弹性滑动的效果。(Handler也是类似的)

对于sleep方法来说,通过在While循环中不断地滑动View和sleep,就可以实现弹性滑动的效果。

四、View 的事件分发

Android事件分发流程 = Activity -> ViewGroup -> View

dispatchTouchEvent

用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响。表示是否消耗此事件

onInterceptTouchEvent

在上述方法内部调用,用来判断是否拦截某个事件,如果当前view拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件。(只有ViewGroup有这个事件)

onTouchEvent

在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接受到事件。

同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件:这个事件序列以Down事件开始,中间含有数量不定的move事件,最终以up事件结束。

按事件进行划分

按照View进行划分

ViewGroup默认不拦截任何事件。ViewGroup的onInterceptTouchEvent默认返回false

View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。

View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。View的longClickable属性默认都为false,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认为false。

onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件。

事件传递过程是有外向内的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DWON事件除外。

五、View 的滑动冲突

常见的滑动冲突场景:

1.外部滑动方向和内部滑动方向不一致;

2.外部滑动方向和内部滑动方向一致;

3.上面两者情况的嵌套

解决方案:

1.外部拦截方法

点击事件先经过父容器的拦截处理,如果父容器需要此事件的话,就拦截;如果不需要的话,就不拦截。

一般都是在onInterceptTouchEvent方法里面进行拦截。

在onInterceptTouchEvent方法中,首先ACTION_DOWN这个事件,父容器必须返回false,即不拦截,这是因为一旦父容器拦截了ACTION_DOWN,那么后续的ACTION_MOVE和ACTION_UP事件都会直接交由父容器处理,这个时候就没法再传到子元素了;

其中是ACTION_MOVE事件,这个事件可以根据需要来决定是否拦截,如果父容器需要拦截就返回true,否则就返回false;

最后是ACTION_UP事件,这里必须返回false。

因为如果父容器在ACTION_UP时返回了true,都会导致子元素无法接收到ACTION_UP事件,这个时候子元素中的onClick事件就无法触发。父容器比较特殊,一旦它开始拦截任何一个事件,那么后续的事件就都会交给它处理,而ACTION_UP作为最后一个事件也必定可以传递给父容器,即便父容器的onInterceptTouchEvent方法在ACTION_UP时返回了false。

2.内部拦截方法

内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理。

要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法稍显复杂。 我们还需要重写子元素的dispatchTouchEvent。在里面结合requestDisallowInterceptTouchEvent一起使用。

  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值