View基础知识(一)
一、什么是View
View
是Android
中所有控件的基类,不管是简单的Button
和TextView
还是复杂的RelativeLayout
和ListView
,它们共同的基类都是View
。
View
是一种界面层的控件的一种抽象,它代表了一个控件。除了View
,还有ViewGroup
,即控件组。在Android
的设计中,ViewGroup
也继承了View
,这就意味着View
本身可以是单个控件也可以是由多个控件组成的一组控件,通过这种关系就形成了View
树的结构,和Web
前端中的DOM
树的概念是相似的。
二、View的位置参数
View
的位置主要由它的四个顶点来决定,分别对应于View
的四个属性:top
、left
、right
、bottom
。其中top
是左上角纵坐标,left
是左上角的横坐标,right
是右下角的横坐标,bottom
是右下角的纵坐标。需要注意的是,这些坐标都是相对于View的父容器来说的,因此它是一种相对坐标,View
的坐标和父容器的关系如下图所示。在Android
中,x
轴和y
轴的正方向分别为右和下。
根据上图,我们很容易得出
View
的宽高和左边的关系:
width = right - left
htight = bottom - top
从Android 3.0
开始,View
增加了额外的几个参数:x
、y
、translationX
和translationY
,其中x
和y
是View
左上角的坐标,而translationX
和translationY
是View
左上角相对于父容器的偏移量。这几个参数也是相对于父容器的坐标,并且translationX
和translationY
的默认值为0,和View
的四个基本的位置参数一样,View
也为它们提供了get/set
方法,这几个参数的换算关系如下所示:
x = left + translationX
y = top + translationY
需要注意的是,View
在平移的过程中,top
和left
表示的是原始左上角的位置信息,其值并不会改变,此时发生改变的x
、y
、translationX
和translationY
这四个参数。
三、MotionEvent和TouchSlop
-
MotionEvent
在手指接触屏幕后所产生的一系列事件中,典型的事件类型有如下几种:-
ACTION_DOWN
--- 手指刚接触屏幕。 -
ACTION_MOVE
--- 手指在屏幕上移动。 -
ACTION_UP
--- 手指从屏幕上松开的一瞬间。
正常情况下,一次手指触摸屏幕的行为会触发一系列的点击事件,考虑如下几种情况:
- 点击屏幕后离开,事件序列为
DOWN
->UP
。 - 点击屏幕滑动一会再松开,事件序列为
DOWN
->MOVE
-> ...>MOVE
->UP
。
上述三种情况是典型的事件序列,同时通过MotionEvent
对象我们可以得到点击事件发生的x
和y
坐标。为此,系统提供了两组方法:getX
/getY
和getRawX
/getRawY
。getX
/getY
返回的是相对于当前View
左上角的x
和y
坐标,而getRawX
/getRawY
返回的是相对于手机屏幕左上角的x
和y
坐标。
-
-
TouchSlop
TouchSlop
是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为你是在进行滑动操作。TouchSlop
是一个常量,和设备有关,在不同设备上的之可能是不同的,通过ViewConfiguration.get(getContext()).getScaledTouchSlop()
即可获取这个常量。当我们在处理滑动时,可以利用这个常量来做一些过滤,比如当两次滑动时间的滑动距离小于这个值,我们就可以认为未达到滑动距离的临界值,因此就可以认为他们不是滑动,这样做就可以有更好的用户体验。
在
Android
源码的res/values/config.xml
中,可以找到这个常量的定义:<!-- Base "touch slop" value used by ViewConfiguration as a movement threshold where scrolling should begin. --> <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
四、VelocityTracker、GestureDetector和Scroller
-
VelocityTracker
速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。它的使用过程很简单,首先,在View
的onTouchEvent
方法中追踪当前单击速度的速度:VelocityTracker velocityTracker = VelocityTracker.obtain(); velocityTracker.addMovement(event);
接着,当我们想知道当前的滑动速度时,这个时候可以采用如下方式来获得当前的速度:
velocityTracker.computeCurrentVelocity(1000); int xVelocity = (int) velocityTracker.getXVelocity(); int yVelocity = (int) velocityTracker.getYVelocity();
在这一步中有两点需要注意:
- 第一点,获取速度之前必须先计算速度,即
getXVelocity
和getYVelocity
这两个方法的前面必须调用computeCurrentVelocity
方法。 - 第二点,这里的速度是指一段时间内手指所滑动的像素数,比如将时间间隔设为1000ms时,在1s内,手指在水平方向从左向右滑动100像素,那么水平速度就是100。
注意速度可以为负数,当手指从右往左滑动时,水平方向速度即为负值。速度的计算可以用如下公式来表示:
速度 = (终点位置 - 起点位置) / 时间段
根据上面的公式再加上
Android
系统的坐标系,可以知道,手指逆着坐标系的正方向滑动,所产生的的速度就为负值。另外computeCurrentVelocity
这个方法的参数表示的是一个时间单元或者说时间间隔,它的单位是毫秒(ms),计算速度时得到的速度就是在这个时间间隔内手指在水平或垂直方向上所滑动的像素块。最后,当不需要使用它的时候,需要调用clear方法来重置并回收内存:
velocityTracker.clear(); velocityTracker.recycle();
- 第一点,获取速度之前必须先计算速度,即
-
GestureDetector
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。下面简单的讲解一下如何使用GestureDetector
首先,需要创建一个
GestureDetector
对象并实现OnGestureListener
接口,根据需要还可以实现onDoubleTapListener
从而能够监听双击行为:GestureDetector gestureDetector = new GestureDetector(this, gestureListener); //解决长按屏幕后无法拖动的现象 gestureDetector.setIsLongpressEnabled(false);
接着,接管目标
View
的onTouchEvent
方法,在待监听View的onTouchEvent
方法中添加如下实现:boolean consume = gestureDetector.onTouchEvent(event); return consume;
做完了上面这步,我们就可以有选择性地实现
onGestureListener
和onDoubleTapListener
中的方法了,这两个接口中的方法介绍如下表所示:方法名 描述 所属接口 onDown 手指轻轻触摸屏幕的一瞬间,由1个 ACTION_DOWN
触发onGestureListener onShowPress 手指轻轻触摸屏幕,尚未松开或拖动,由1个 ACTION_DOWN
触发 * 注意与onDown()的区别,它强调的是没有松开或者拖动的状态onGestureListener onSingleTapUp 手指(轻轻触摸屏幕后)松开,伴随着1个 MotionEvent ACTION_UP
而触发,这是点击行为onGestureListener onScroll 手指按下屏幕并拖动,由1个 ACTION_DOWN
,多个ACTION_MOVE
触发,这是拖动行为onGestureListener onLongPress 用户长久的按着屏幕不放,即长按 onGestureListener onFling 用户按下触摸屏,快速滑动后松开,有1个 ACTION_DOWN
、多个ACTION_MOVE
和1个ACTION_UP
触发,这是快递滑动行为onGestureListener onDoubleTap 双击,由2次连续的单击组成,它不可能和 onSingleTapConfirmed
共存onDoubleTapListener onSingleTapConfirmed 严格的单击行为 *主要它和 onSingleTapUp
的区别,如果触发了onSingleTapConfirmed
,那么后面不可能再紧跟着另一个单击行为,即这只可能是单击,而不可能是双击中的一次单击onDoubleTapListener onDoubleTapEvent 表示发生了双击行为,在双击的期间, ACTION_DOWN
、ACTION_MOVE
和ACTION_UP
都会触发此回调onDoubleTapListener 表中方法很多,但并不是所有的方法都时常被用到,在日常开发中,比较常用的有:
onSingleTapUp
(单击)、onFling
(快速滑动)、onScroll
(拖动)、onLongPress
(长按)和onDoubleTap
(双击)。另外,在实际开发中,可以不使用
GestureDetector
,完全可以自己在View
的onTouchEvent
方法中实现所需的监听。如果只是监听滑动相关的建议就在onTouchEvent
中实现,如果要监听双击这种行为的话,那么就使用GestureDetector
。 -
Scroller
弹性滑动对象,用于实现View
的弹性滑动。我们知道,当使用View
的scrollTo
/scrollBy
方法来进行滑动时,其过程是瞬间完成的,这个没有过渡效果的滑动用户体验不好。这是就可以使用Scroller
啦实现有过渡效果的滑动,其过程不是瞬间完成的,而是在一定的时间间隔内完成的。Scroller
本身无法让View
弹性滑动,它需要和View
的computeScroll
方法配合使用才能共同完成这个功能,下面是实现的典型代码:private Scroller mScroller = new Scroller(mContext); //缓慢滚动到指定位置 private void smoothScrollTo(int destX, int destY) { int scrollX = getScrollX(); int delta = destX - scrollX; // 1000ms 内滑向destX,效果就是慢慢滑动 mScroller.startScroll(scrollX,0,delta,1000); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); postInvalidate(); } }