View基础知识
View的位置参数:
top、left、right、bottom,分别对应View的左上角和右下角相对于父容器的横纵座标值。从Android 3.0开始,view增加了x、y、translationX、translationY四个参数,这几个参数也是相对于父容器的座标。x和y是左上角的座标,而translationX和translationY是view左上角相对于父容器的偏移量,默认值都是0。
这几个参数的换算公式:
View在平移过程中,top和left表示原始左上角的位置信息,其值并不会发生改变
MotionEvent和TouchSlop
MotionEvent是指手指接触屏幕后所产生的一系列事件,主要有ACTION_UP、ACTION_DOWN、ACTION_MOVE等。
正常情况下,一次手指触屏会触发一系列点击事件,主要有下面两种典型情况:
- 点击屏幕后离开,事件序列是ACTION_DOWN->ACTION_UP;
- 点击屏幕后滑动一会再离开,事件序列是ACTION_DOWN->ACTION_MOVE->ACTION_MOVE-> … ->ACTION_UP;
TouchSlope是系统所能识别出的可以被认为是滑动的最小距离,获取方式是
ViewConfiguration.get(getContext()).getScaledTouchSlope()。
VelocityTracker
VelocityTracker用于追踪手指在滑动过程中的速度,包括水平和垂直方向上的速度。速度可能为负值,例如当手指从屏幕右边往左边滑动的时候。此外,速度是单位时间内移动的像素数,单位时间不一定是1秒钟,可以使用方法computeCurrentVelocity(xxx)指定单位时间是多少,单位是ms。例如通过computeCurrentVelocity(1000)来获取速度,手指在1s中滑动了100个像素,那么速度是100,即100(像素/1000ms)。如果computeCurrentVelocity(100)来获取速度,在100ms内手指只是滑动了10个像素,那么速度是10,即10(像素/100ms)。
使用步骤:
1.获取VelocityTracker实例:
VelocityTracker mVelocityTracker = VelocityTracker.obtain();
2.将MotionEvent事件纳入到VelocityTracker中:
mVelocityTracker.addMovement(event);
3.计算瞬时速度:
mVelocityTracker.computeCurrentVelocity(units,maxVelocity);
4.获取xy方向上的速度:
float vX = mVelocityTracker.getXVelocity(pointId);
float vY = mVelocityTracker.getYVelocity(pointId);
5.回收VelocityTracker:
mVelocityTracker.clear();
mVelocityTracker.recycle();
6.使用示例:
/**
* 获取速度追踪器
* @return
*/
private VelocityTracker getVelocityTracker()
{
if(mVelocityTracker == null)
{
mVelocityTracker = VelocityTracker.obtain();
}
return mVelocityTracker;
}
/**
* 回收速度追踪器
*/
private void recycleVelocityTracker()
{
if(mVelocityTracker != null)
{
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
//1.获取速度追踪器
getVelocityTracker();
//2.将当前事件纳入到追踪器中
mVelocityTracker.addMovement(event);
int pointId = -1;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.i(TAG,"ACTION_DOWN");
pointId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG,"ACTION_MOVE");
//3.计算当前速度
mVelocityTracker.computeCurrentVelocity(1000,mMaxFlingVelocity);
//获取x y方向上的速度
float vX = mVelocityTracker.getXVelocity(pointId);
float vY = mVelocityTracker.getYVelocity(pointId);
Log.i(TAG,"vX = "+vX+",vY = "+vY);
break;
case MotionEvent.ACTION_UP:
Log.i(TAG,"ACTION_UP");
//4.回收速度追踪器
recycleVelocityTracker();
break;
default:
break;
}
return super.onTouchEvent(event);
}
GestureDetector
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。
- 首先需要实现GestureListener接口
mGestureDetector = new GestureDetector(this, new GestureScrollListener());
- 在TouchEvent方法中接管手势
mGestureDetector.onTouchEvent(ev)
View的滑动
常见的实现view的滑动的方式有三种:
- 1.通过view本身提供的scrollTo和scrollBy方法:操作简单,适合对view内容的滑动;
- 2.通过动画给view施加平移效果来实现滑动:操作简单,适用于没有交互的view和实现复杂的动画效果;
- 3.通过改变view的LayoutParams使得view重新布局从而实现滑动:操作稍微复杂,适用于有交互的view
scrollTo和scrollBy
- scrollTo(int x, int y) 是将View中内容滑动到相应的位置
- scrollBy(int x, int y)其实是对scrollTo的包装,移动的是相对的位置
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);
}
使用动画
- 使用动画来移动view主要是操作view的translationX和translationY属性,既可以使用传统的view动画,也可以使用属性动画。
- 使用动画还存在一个交互问题:在android3.0以前的系统上,view动画和属性动画,新位置均无法触发点击事件,同时,老位置仍然可以触发单击事件。从3.0开始,属性动画的单击事件触发位置为移动后的位置,view动画仍然在原位置。
- 动画兼容库nineoldandroids中的ViewHelper类提供了很多的get/set方法来为属性动画服务,例如setTranslationX和setTranslationY方法,这些方法是没有版本要求的。
ViewHelper.getTranslationX(view);
ViewHelper.setTranslationY(view,100);
ViewHelper.setTranslationX(view,100);
改变布局参数
通过params来设置margin改变位置
Scroller
- Scroller:弹性滑动对象
- v4中ScrollerCompat类兼容库
- 需要computeScroll配合更新位置
执行滑动:
mScroller.startScroll( startX, startY,dx, dy, duration);
控制滑动这个流程:
@Override
public void computeScroll() {
super.computeScroll();
// 判断Scroller是否执行完毕
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo( mScroller.getCurrX(), mScroller.getCurrY());
// 通过重绘来不断调用computeScroll
invalidate();//很重要
}
}
computeScroll:主要功能是计算拖动的位移量、更新背景、设置要显示的屏幕(setCurrentScreen(mCurrentScreen);)。
重写computeScroll()的原因:
调用startScroll()是不会有滚动效果的,只有在computeScroll()获取滚动情况,做出滚动的响应
computeScroll在父控件执行drawChild时,会调用这个方法