View的概述
- View是Android中所有控件的基类,
不管是简单的Button和TextView还是复杂的RelativeLayout和ListView,
它们的共同基类都是View
。 - View是一种界面层的控件的一种抽象,它代表了一个控件。
- ViewGroup内部可以包含许多个控件,即一组View。
ViewGroup也继承了View,
即View本身就可以是单个控件也可以是由多个控件组成的一组控件,
通过这种关系就形成了View树
的结构,
这和Web前端中的DOM树的概念是相似的。
上层的控件要负责测量与绘制
下层的控件,并传递交互事件
。
在每棵View树
的顶部都存在着一个ViewParent
对象,
它是整棵View树
的核心所在,
所有的交互管理事件
都由它来统一调度和分配
,
从而对整个视图进行整体控制
。
Button显然是个View,
而LinearLayout不但是一个View而且还是一个ViewGroup,
而ViewGroup内部是可以有子View的,这个子View同样还可以是ViewGroup,依此类推。明白View的这种层级关系有助于理解View的工作机制。
View的位置参数
- Android坐标系:以屏幕的左上角为坐标原点,向右为x轴增大方向,向下为y轴增大方向。
- View的位置主要由它的四个顶点来决定,
分别对应于View的四个属性
:top、left、right、bottom
; - 其中top是左上角纵坐标,left是左上角横坐标,
right是右下角横坐标,bottom是右下角纵坐标。 - 注意这些坐标都是相对于
View的父容器
来说的,因此它是一种相对坐标
,
View的坐标和父容器的关系如下图:
根据上图,
可以得出View的宽高
和坐标
的关系
:
width = right - left
height = bottom - top
关于如何得到View的这四个参数
,
在View的源码
中它们对应于mLeft、mRight、mTop和mBottom
这四个成员变量,获取方式:
Left=getLeft();
Right=getRight();
Top=getTop;
Bottom=getBottom();
---
width=getWidth();
height=getHeight();
从Android3.0开始,
View增加了额外的几个参数:x、y、translationX
和translationY
,
x
和y
是View左上角的坐标
,
translationX
和translationY
是View左上角
相对于父容器
的偏移量
。这几个参数也是
相对于父容器的坐标
!!!!
并且translationX
和translationY
的默认值是0
,
View也为它们提供了get/set
方法,下面是这几个参数的关系
:
x=left+translationX
y=top+translationY
- 注意,
View
在平移
的过程中,
top
和left
表示的是原始左上角
的位置信息
,其值
并不会发生改变
!!!
此时发生改变
的是x、y、translationX
和translationY
这四个参数!!!
MotionEvent和TouchSlop
1. 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坐标。
2. TouchSlop
概念:
系统所能识别出
的被认为
是滑动
的最小距离
,
即当手指在屏幕上滑动时
,如果两次滑动
之间的距离
小于这个常量
,
那么系统就不认为
你是在进行滑动操作
。
原因:滑动的距离
太短,系统不认为
它是滑动。这是一个
常量
,和设备
有关,在不同设备上这个值可能是不同的,
通过ViewConfiguration. get(getContext()).getScaledTouchSlop()
可获取这个常量。TouchSlop
的意义
:
处理滑动
时,可以利用这个常量来做一些过滤
;
比如当两次滑动事件的滑动距离小于这个值
,
我们就可以认为未达到滑动距离的临界值
,
因此就可以认为它们不是滑动
,
这样做可以有更好的用户体验
!!!源码中有这个常量的定义,
在frameworks/base/core/res/res/values/config.xml
文件中,如下所示。这个“config_viewConfigurationTouchSlop”对应的就是这个常量的定义。
8dp
VelocityTracker、GestureDetector和Scroller
1. VelocityTracker
概念:
速度追踪,用于追踪手指
在滑动过程中
的速度
,
包括水平和竖直方向
的速度。
使用过程:
首先,在View的onTouchEvent方法
中追踪当前单击事件
的速度
:
VelocityTracker velocityTracker = VelocityTracker.obtain();//实例化一个VelocityTracker 对象
velocityTracker.addMovement(event);//添加追踪事件
- 接着在对
ACTION_UP事件
的处理中 获取当前的速度
。
注意这里计算的是1000ms时间(即1s)
间隔移动的像素值,
假设像素是100,即速度是每秒100像素。
在1s内,手指在水平方向从左向右滑过100像素,那么水平速度就是100。
另外,如在水平方向上,
手指逆着坐标系的正方向(从右往左滑动)滑动,所产生的速度为负值,
顺着正反向(从左往右滑动)滑动,所产生的速度为正值。
velocityTracker .computeCurrentVelocity(1000);//获取速度前先计算速度,这里计算的是在1000ms内
float xVelocity = velocityTracker .getXVelocity();//得到的是1000ms内手指在水平方向从左向右滑过的像素数,即水平速度
float yVelocity = velocityTracker .getYVelocity();//得到的是1000ms内手指在垂直方向从上向下滑过的像素数,即垂直速度
注意,
获取速度之前必须先计算速度,
即getXVelocity
和getYVelocity
这两个方法的前面
必须要调用computeCurrentVelocity
方法;!!速度的计算可以用如下公式来表示:
速度=(终点位置-起点位置)/时间段
computeCurrentVelocity()
的方法参数表示的是
一个时间单元
或者说时间间隔
,单位是毫秒(ms)
,
计算速度时得到的速度
就是在这个时间间隔内
手指在水平或竖直方向上所滑动的像素数。
针对上面的例子,
如果我们通过velocityTracker.computeCurrentVelocity(100)
来获取速度,
那么得到的速度就是手指在100ms内所滑过的像素数,
假设返回的是10,
则水平速度就成了10像素/每100ms(这里假设滑动过程是匀速的),
即水平速度为10。
- 最后不需要使用它时,需调用clear方法来重置,并回收内存:
velocityTracker.clear();
velocityTracker.recycle();
GestureDetector
概念:手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。
使用过程:
首先,
需要创建一个GestureDetector对象
,
并实现OnGestureListener接口
,
根据需要还可以实现OnDoubleTapListener
从而能够监听双击行为
:!!!
GestureDetector mGestureDetector = new GestureDetector(this);
//解决长按屏幕后无法拖动的现象
mGestureDetector.setIsLongpressEnabled(false);
接着,
接管目标View的onTouchEvent
方法,
在待监听View的onTouchEvent方法
中添加如下实现:
boolean consume = mGestureDetector.onTouchEvent(event);
return consume;
做完以上两步后,
即可有选择地实现OnGestureListener
和OnDoubleTapListener
中的方法了,
这两个接口中的方法介绍如下表:
- 实际开发中,
可以不使用GestureDetector,
可以自己在View的onTouchEvent方法中实现所需的监听,看个人的喜好。
建议,
如果只是监听滑动相关的,建议自己在onTouchEvent中实现,
如果要监听双击这种行为的话,那么就使用GestureDetector。
Scroller
概念:弹性滑动对象,用于实现View的弹性滑动。
当使用
View
的scrollTo/scrollBy
方法来进行滑动
时,
其过程
是瞬间完成
的,
这个没有过渡效果
的滑动
用户体验
不好。
此时可使用Scroller
来实现有过渡效果的滑动
,
其过程不是瞬间完成
的,
而是在一定的时间间隔内完成
的。Scroller
本身无法让View弹性滑动
,
它需要和View的computeScroll方法
配合使用才能完成这个功能。
使用Scroller,其典型代码是固定的:
Scroller scroller = new Scroller(mContext);
// 缓慢滚动到指定位置
private void smoothScrollTo(int destX,int destY) {
int scrollX = getScrollX();
int delta = destX -scrollX;
// 1000ms内滑向destX,效果就是慢慢滑动
mScroller.startScroll(scrollX,0,delta,0,1000);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
参考:
- 《Android开发艺术探索》
- 要点提炼| 开发艺术之View