View详解之二View的事件体系

来源:互联网 发布:modbus单片机程序 编辑:程序博客网 时间:2024/05/16 08:55

这周接着上次的学习,继续来学习View相关知识,View的事件体系。

首先,掌握几个知识点。

View的位置参数

先上图,

view的坐标

左图中left 、top、right、bottom分别是四个顶点相对于父容器的值,

右图中当view反生平移时,view的top值是不会改变的x、y、transitionX、transitionY会反生改变,并且相对于父容器而言。

y = top + transitionY;x= left + transitionX;

当在屏幕上点击或者滑动时,可以得到手指的坐标,getX()getY()得到的是相对View的内部坐标,getRawX()getRawY()得到的是相对于整个屏幕的坐标。

touchslop 、velocityTracker、GestureDetector、Scroller

touchslop是系统认定触摸事件为滑动的最小距离

VelocityTracker是用于 监听滑动速度的类,用法:

1.在action_down中获得VelocityTracker对象,并追踪该事件,

2.computeCurrentVelocity(1000)设定时间间隔,该类就是根据改时间间隔内手指滑动的像素点来计算速度。

3.不需要使用时,clear(),recycle();

GestureDector用于处理用户的单击,滑动,长按,双击等。

在View的ontouchevent中接管event事件,即可 处理触摸事件了。

作用和自己实现ontouchevent的事件处理类似。如果仅处理滑动可以自己处理ontouchevent,处理双击等事件可以使用GestureDector。

Scroller用于View的内部内容的滑动,使用有固定的模式,具体原理请看下文。

View的滑动的几种方法

1.ScrollTo、ScrollBy

ScrollBy内部是调用ScrollTo实现基于现在位置滑动一定距离来实现相对滑动的,滑动的仅仅是view的内容

适用于内容滑动情况

前者实现的是绝对滑动,后者实现的是相对滑动,滑动的距离都是View内部的内容边界相对于view的边界的距离值,如果view边界在内容边界的正方向则mScrollX,mScrollY为正值,否则为负直。具体看图

这里写图片描述

所以Scrollto()参数的正负和坐标系一样,而View的mScrollX属性则是相反的。

2.动画

transitionX、transitionY属性实现平移,Vew的补间动画是不能真实移动View的,使用兼容库依然不能实现属性动画的真实移动

适合于复杂一点的滑动动画

3.改变View的布局参数

比如marginLeft属性等

适用于需要View进行交互的滑动

滑动之实现弹性滑动

1.Scroller

Scroller 本身不能实现滑动,他只是保存了滑动需要的参数,调用startScroll()然后invalidate()进行重绘,View的重绘会调用computeScroll()方法,该方法需要自己实现,需要在里面调用scroller的computeScrollOffset使用插值器根据持续时间和目的距离,计算当前需要滑动的滑动的距离,然后再次调用postinvalidate()实现重绘然后再次滑动小段距离,直到结束。

2.动画

动画本身具有弹性滑动的效果

可以使用属性动画的事件监听器实现View的滑动,在事件监听中使用scroll实现滑动

3.使用延时策略

延时可以用,handler、view的postdelayed()方法发送延时消息,并在消息处理中再次发送延时消息,实现循环。也可以使用线程的sleep()(一般是子线程),

比如幻灯片,

更多滑动方法请看

View的事件分发机制

View的事件分发是View的核心也是难点,弄懂了这个才能学会滑动事件的冲突处理。

View和Viewgroup的事件处理中有这么几个重要的步骤:

diapatchTouchEvent(),

进行事件分发,如果时间传递到该view那么该方法一定会被调用,返回结果表示是否消耗该事件,

onInterceptTouchEvent()

返回结果用于判断是否拦截某个事件,如果当前View拦截了事件,那么在同一个事件序列中,此方法不会再次被调用。

onTouchevent()

用来处理点击事件,返回结果表示是否消耗当前事件。,如果不消耗那么同一事件序列,gaiview不会再次接收到事件,View默认是返回true,即消耗当前事件。

三个方法的处理过程如下伪代码:

boolean dispatchTouchEvent(){    boolean  consume = falseif(onInterceptTouchEvent){        consume = onTouchEvent();    }else{        consume = child.dispatchTouchEvent();    }    return consume;}

ontouch()

用于在外部处理touch事件,优先级高于onTouchevent,如果返回true,则不会执行onTouchevent。

onclick在onTouchevent的action_up事件中执行。

点击事件的传递过程遵循的顺序:Activity->Window->ViewGroup->…->View;

如果最底层的View在onTouchevent中返回false,即不消耗该事件,那么该事件会返回到父容器的onTouchevent中处理,如果最后没被不消耗那么会返回到activity的onTouchevent中处理。

activity的dispatchTouchEvent将事件分发交个window处理,如果返回true则表示事件被消耗了,返回false则调用调用其onTouchevent()。

window将事件分发给DecorView处理,

结论:

1.如果View==拦截==了onIntercept()某事件包括action_down,那么后续的事件都直接由它处理onTouchevent。onIntercept也不会在被调用是否需要拦截了。

2.如果某个view==不消耗==action_down事件,那么后续事件都不会传递给该View,而是有父容器的onTouchevent处理。默认是会消耗的。

3.view可以干预通过requestDisAllowIntercepttouchevent()方法干预父容器的事件拦截,但不能作用于ViewGroup对Action_down事件的拦截,因为每次都会重置该标志位。拦截了action_down,后续的事件都会被拦截

4.onIntercept不是一定会被调用的,ontouchevent也是,如果要提前对事件处理应该在dispatch中进行。

View对点击事件的处理

首先执行onTouch()如果返回false执行onTouchevent(),会执行performclick执行点击事件,设置了点击事件之后会自动将View设为clickable;

View的滑动冲突

滑动冲突的场景:

1.水平和竖直方向交叉,冲突

2.水平和水平同方向冲突

3.多层嵌套冲突

这里写图片描述

viewpager 处理了类似场景一的情况,重点在==滑动规则的确定==

场景一,可以采用判断滑动方向的方式进行拦截,比如,与水平方向的夹角,水平和竖直方向的距离差,或者速度差

场景二,可以根据业务上判断,在某个条件下需要内部拦截滑动,某些条件需要外部拦截。

场景三,则复用前两者。

这里用到事件分发机制,进行内部和外部事件处理的切换。

通用的滑动冲突处理方法

1.外部拦截法:

思想:父容器在需要时拦截事件,不需要时不拦截,由子View处理,子view不需要判断

实现:

复写父容器的onInterceptTouchEvent()方法。

action_down返回false,否则将拦截子view的全部事件,

action_move,中判断是否需要拦截,需要的话返回true,

action_up,返回false,父容器只要拦截了up事件之前的事件就可以得到up事件,所以不需要拦截up,如果拦截子view的up事件得不到处理,不能处理click事件。

伪代码:

boolean onInterceptTouchEvent(){    boolean intercepted = false;    int x = event.getx();    int y = event.getY();    switch(event.getAction()){    case action_down:        intercepted = false;        break;    case action_move:    if(需要父容器拦截)        intercepted= true;    else{        intercepted = false;    }        break;    case action_up:        intercepted = false;        break;    }    lastX=x;    lastY = y;    return intercepted;}

2.内部反拦截法

思想:父容器拦截move和up事件不能拦截action_down,该事件不受flag_disallow_intercept标志位控制,子view根据需要使用requestDisallowInterepttouchEvent()允许和禁止父容器的拦截。

实现:

子View复写dispatchTouchEvent(),在move事件中判断事件有子容器还是父容器需要,如果子View需要则调用parent.requestDisallowInterceptEvent(true),否则传入false。
父View复写onInterceptTouchEvent()拦截除down以外的事件。

好了,原理先到这。

3 0
原创粉丝点击