View事件体系

2018-12-16  本文已影响23人  安卓小白之小楼又东风

标签(空格分隔): android


View的定义

View是android中所有控件的基类,不管是简单的Button还是TextView或者复杂的RelativeLayout和ListView,它们的基类都是View。

view的位置参数

view的位置坐标和父容器的关系view的位置坐标和父容器的关系

height = bottom-top;
width = right-left;

如何获取它们:
left = getLeft();
right = getRight();
top = getTop();
bottom = getBottom();

其他的位置参数:x,y,translationX,translationY。

MotionEvent 和 TouchSlop

1、MotionEvent

通常情况一次手机触摸的触发事件:

通过MotionEvent对象获取下,下,x,y坐标。

getX/getY返回当前View左上角的下,x、y坐标,getRawX/getRawY返回手机屏幕的x、y坐标。

2、TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离。

不同的设备的这个值不同,获取这个常量需要用:ViewConfiguration.get(getContext()).getScaledTouchSlop().

VelocityTracker GestureDetector Scroller

1、VelocityTracker
速度追踪,用于追踪手指在滑动过程中的速度。(水平速度和竖直速度)
获取滑动速度代码:

VelocityTracker velocityTracker = VelocityTracker.obtain();//创建对象
velocityTracker.addMovement(event);//添加事件 velocityTracker.computeCurrentVelocity(1000);//设置事件间隔
//获取速度(像素)
int xVelocity = (int)velocityTracker.getXVelocity();
int yVelocity = (int)velocityTracker.getYVelocity();

重置、回收内存:

 velocityTracker.clear();
 velocityTracker.recycle();

注意:手指逆着坐标系的正方向滑动,产生速度是负值,正负号表示方向。
2、GestureDetector
手势检测,用于辅助用户单击、滑动、长按、双击等行为.
使用过程:
1、创建GestureDetetor对象,实现OnGestureListener、OnDoubleTapListener接口。
2、接管目标View的onTouchEvent方法.

一些回调接口:(参考于这篇博文)
1.OnGestureListener,这个Listener监听一些手势,如单击、滑动、长按等操作:

2.OnDoubleTapListener,这个Listener监听双击和单击事件。

3.OnContextClickListener,很多人都不知道ContextClick是什么,我以前也不知道,直到我把平板接上了外接键盘——原来这就是鼠标右键。。。

4.SimpleOnGestureListener,实现了上面三个接口的类,拥有上面三个的所有回调方法。

3、Scroller
弹性滑动对象,用于实现View的弹性滑动。
用Scroller来实现有过渡效果的滑动,其过程不是瞬间完成的。

View的滑动

常见的滑动:

1、使用ScrollTo、ScrollBy

scrollBy实际上是调用了scrollTo方法,实现了基于当前位置的相对滑动,srcollTo是实现了基于参数的绝对滑动。
scrollTo和scrollBy只能改变View内容的位置而不能改变View在布局中的位置。
mScrollX,mScrollY的单位是像素,可以是负值,符号表示方向。
重要的源码:

public void scrollTo(int x,int y){
    if(mScrollX != x || mScrollY != y){
       int oldX = mScrollX;
       int oldY = mScrollY;
       mScrollX = x;
       mScrollY = y;
       invalidateParentCaches();
       onScrollChanged(mScrollX,mScrollY,oldX,oldY);
       if(!awakenScrollBars()){
          postInvalidateOnAnimation();
       }
    }
}

public void scrollBy(int x,int y){
  scrollTo(mSrollX+x,mScrollY+y);
}

2、使用动画

使用动画移动View,主要是操作View的translationX和translationY属性,可采用传统动画和属性动画。
View动画已经逐渐被废弃,不是改变view的位置,动画完成瞬间,回到原位置。
属性动画:

ObjectAnimator.dfFloat(targetView,"translationX",0,100).setDuration(100).start();

3、使用延时策略

核心思想:通过发送一系列的延时消息从而达到一种渐进式的效果,可以使用Handler、view的postDelayed方法,也可以使用线程中的sleep方法。

View的事件分发机制

定义:当一个MotionEvent产生后,系统需要把这个事件传递给一个具体的View,而传递的过程就是分发过程。

常见的一些方法:
dispatchTouchEvent:用于进行事件的分发,返回值为boolean,表示是否消耗当前事件。
onInterceptTouchEvent:用来判断是否拦截某个事件,返回值为boolean,表示是否拦截当前事件。
onTouchEvent:用于处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则同一事件序列中,当前View无法再次接收到事件。

点击事件产生后遵循顺序:Activity--》window--》View

同一事件序列是指从手指接触屏幕开始到离开屏幕这个过程中所产生一系列事件,这个事件序列从down开始,中间有一系列的move,最终以up事件结束。

1、Activity对点击事件的分发过程

Activity # dispatchTouchEvent

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

首先事件开始交给Activity所附属的Window进行分发,返回true,返回false就意味着事件没人处理,所有的View的onTouchEvent都返回了false,那么Activity的onTouchEvent就会被调用。

window的唯一实现类是PhoneWindow,PhoneWindow将事件传递给了DecorView。DecorView继承于FrameLayout,是父View。

2、顶级View对点击事件的分发过程

主要逻辑:如果顶级ViewGroup拦截事件,就是onInterceptTouchEvent返回true,事件由ViewGroup处理,这时如果ViewGroup的mOnTouchListener被设置,则onTouch会被调用。否则onTouchEvent会被调用。在onTouchEvent中,设置了mOnclickListener,则onClick方法会被调用。如果ViewGroup不拦截事件,事件会传递给它所在的点击事件链的上的子View,这时子View中的dispatchTouchEvent方法会被调用。

ViewGroup在事件分发时,如果时ACTION_DOWN会被重置FLAG_DISALLOW_INTERCEPT这个标记位,将导致子View中设置标记位无效。

当ViewGroup决定拦截事件后,后续点击事件将会默认交给它处理并且不会调用它的onInterceptTouchEvent方法。onInterceptTouchEvent不是每次事件都被调用的,如果要提前处理所有点击事件,要选择dispatchYouchEvent方法,只有这个方法能够确保每次都会调用。

是否接受点击事件主要由两点衡量:子元素是否播放动画和点击事件的坐标是否落在子元素的区域内。

通过for循环遍历ViewGroup中子元素,如果子View满足接受点击事件的条件,子元素直接调用dispatchTouchEvent方法,如果返回值为true,事件就被分发到子元素,mFirstTouchTarget被赋值跳出for循环。

mFirstTouchTarget是一种单链表结构,如果它为null,ViewGroup就默认拦截接下来同一事件序列的所有点击事件。遍历所有的子元素后,没有被合适的处理就说明ViewGroup中没有子元素或者子元素的onTouchEvent返回为false。

3、View对点击事件的处理过程

优先级比较:
OnTouchListener > onTouchEvent > onClick

只要View的CLICKBLE和LONGCLICKBLE有一个是true,View就会消耗这个事件,即onTouchEvent返回true。当ACTION_UP发生时,会触发performClick方法,如果View被设置了onClickListener,那么performClick内部会调用它的onClick方法。

滑动冲突

1、外部拦截法:通过父容器需要对此事件拦截,如果不需要就不拦截。

在ACTION_DOWN必须返回false,否则一旦拦截,后续的事件序列都会被拦截,没法传递给子View了,ACTION_MOVE根据需求去返回false或true,ACTION_UP必须返回false,其本身没有意义,否则子元素无法接受up事件,onClick没有被触发。

2、内部拦截法:父容器不拦截任何事件,所有的事件都传递给子元素,直接被消耗掉,否则返回给父元素,需要配合requestDisallowInterceptTouchEvent。

上一篇 下一篇

猜你喜欢

热点阅读