View 的一些知识点
View 是一个界面层控件的一种抽象,它代表一个控件。由于 ViewGroup 也是继承 View ,这也就意味这 View 本身可以是单个控件也可以是多个控件组成的一组控件。
决定 View 的位置参数
一种方式,相对于父容器的来说
- top getTop()
- left getLeft()
- right getRight()
- bottom getBottom()
关系如下:
View 的宽高和他们的关系是
width=right-left
height=bottom-top
额外增加的参数 ,也是相对于父容器的坐标
- x
- y
- translationX
- translationY
x , y 表示 View 左上角的坐标
translationX 和 translationY 表示 View 左上角相对于父容器的偏移量`
换算关系
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
得到点击时间发生的 x 和 y 坐标,系统提供两种方法:
- getX()和 getY() 返回的是相对 当前 View 左上角 的 x 和 y 坐标
- getRawX()和 getRawY() 返回相对于的 手机屏幕左上角 x和 y 坐标
TouchSlop
系统所能识别出的被认为是滑动的最小距离,也就是说如果两次滑动距离小于这个值,系统不认为你是在进行滑动操作。这是一个常量,和设备有关。获取这个常量的代码:
ViewConfiguration.get(getContext()).getScaledDoubleTapSlop()
这个常量的意义在于做一些过滤,小于这个值的滑动认为无效。在源码中定义的位置为
framework/base/core/res/res/values/config.xml中
<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
VelocityTracker, GestrureDetecotr 和Scroller
速度追踪VelocityTracker
用于追踪手指在滑动过程中的速度。包括水平和竖直方向的速度。 使用步骤主要包括以下几步
- 在 onTouchEvent 中追踪当前事件的速度
- 根据时间计算速度
- 得到水平或者竖直方向的速度,
- 清理并回收资源 注意: 速度可以为负数,从右往左滑动,水平方向速度为负值
public boolean onTouchEvent(MotionEvent event) {
VelocityTracker velocityTracker=VelocityTracker.obtain();
velocityTracker.addMovement(event);
//获取速度之前必须计算速度,速度是一段事件内手指所滑动过的像素数,
velocityTracker.computeCurrentVelocity(1000);//计算 1000ms 内手指滑动过的像素数
int xVeleocity=(int) velocityTracker.getXVelocity();//水平方向的速度,速度可以为负数,从右往左滑动,水平方向速度为负值
int yVeleocity=(int) velocityTracker.getYVelocity();
//不需要使用的时候,清理并回收内存
velocityTracker.clear();
velocityTracker.recycle();
...
}
手势检测 GestureDetector
主要用于辅助检测用户的单击,滑动,长按,双击等行为, 使用过程:
- 创建 GextureDetector 对象,并实现 OnGestureListener 接口,还可以根据需要实现 OnDoubleTapListener 从而能监听双击行为。
GestureDetector gestureDetector=new GestureDetector(this); //解决长按屏幕后无法拖动的现象 gestureDetector.setIsLongpressEnabled(false);
- 在监听 View 的 onTouchEvent 方法中实现
public boolean onTouchEvent(MotionEvent event) { boolean consume =gestureDetector.onTouchEvent(event); return consume }
- OnGestureListener和 OnDoubleTapListener 的方法介绍
常用方法:
- onSingleUp 单击
- onFling 快速滑动
- onScroll 拖动
- onLongPress 长按
- onDoubleTap 双击
使用建议:如果只用监听滑动相关的,建议自己在 onTouchEvent 中实现,如果要监听双击这种行为的话,那么就使用GestureDetector
弹性滑动对象 Scroller
用于实现 View 的弹性滑动。因为在使用scrollTo()/scrollBy()方法进行滑动,是没有滑动效果的,用户体验不好,可以使用 Scroller 配合 View 的 computeScroll() 方法才实现过渡动画效果。典型代码固定格式
Scroller scroller=new Scroller(context)
public void computeScroll() {
if(scroller.computeScrollOffset()){
smoothScrollTo(scroller.getCurrX(),scroller.getCurrY());
postInvalidate();
}
}
//缓慢平滑的移动到指定位置
private void smoothScrollTo(int destX,int destY){
int scrollX=getScrollX();
int delta=destX-scrollX;
//1000ms内滑动 destx ,效果就是慢慢滑动
scroller.startScroll(scrollX, 0 , delta , 0 ,1000);
invalidate();
}
View 的滑动
不管多么绚丽的滑动效果,归根到底,都是由不同的滑动外加一些特效所组成的。因此掌握滑动的方法是实现绚丽的自定义控件的基础,通过一下三种方式可以实现 View 的滑动
- 通过 View 本身的 scrollTo() 和 scrollBy() 方法
- 通过给 View 施加平移效果
- 通过改变 View 的 LayoutParams 使得 View 重新布局而实现滑动
scrollTo()/scrollBy()
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(mScrollX + x, mScrollY + y);
}
说明以下几点:
- scrollBy() 实际上是调用的 scrollTo() ,而 scrollTo() 是实现了基于所传递的参数的绝对滑动。只能改变 View 内容的位置,而不能改变 View 在布局中的位置。
- View边缘: View的位置,由四个顶点组成,
- View内容边缘:View中内容的边缘,
- mScrollX : 等于 View 左边缘和 View 内容左边缘在水平方向的距离,单位像素,当 View 左边缘在 View 内容左边缘的右边时候,值为正,反之为负,换句话说从左向右滑动,值为负
- mScrollY : 等于 View 上边缘和 View 内容上边缘在竖直方向的距离,单位像素,从上往下滑,值为负
动画
通过动画我们能让一个 View 进行平移,而平移就是一种滑动。主要操作就是 View 的 translationX 和 translationY 可以使用传统 View 动画,也可以采用属性动画。采用属性动画为了兼容3.0以下版本,需要使用开源动画库 nineoldandroids ,例如把一个 VIew 在 100ms 从原始位置移动 100 个像素
-
传统 View 动画
-
属性动画
ObjectAnimator.ofFloat(targetView,"translationX", 0 ,100).setDuration(100).start();
说明:
- 使用动画并不能真正改变 View 的位置参数,只是对 View 影像的操作。
改变布局参数
即改变 layoutParams ,如果想把一个 Button 向右平移100px.只需要将这个 Button 的 layoutParams 的 marginLeft 参数值增加100px
三种滑动方式的对比
方式 | 优点 | 缺点 | 使用场景 |
---|---|---|---|
scrollTo()/scrollBy() | 操作简单,很方便的实现滑动效果且不影响内部元素的点击事件 | 只能滑动 view 的内容,不能滑动 View 本身 | 适合对 View 内容的滑动 |
动画 | android 3.0以上并采用属性动画,没有缺点 | 传统动画或者android3.0一下使用属性动画,都不能改变 View 本身属性 | 没有交互的 VIew 和实现赋值的动画效果 |
改变布局参数 | 既能滑动 View 的内容,又能滑动 View 本身 | 使用麻烦 | 适用于有交互的View |
搬运地址:
Android 开发艺术探索
既已览卷至此,何不品评一二: