零碎的知识
View的位置参数
- x,控件左上角在父容器中的x坐标像素值
- y,控件左上角在父容器中的y坐标像素值
- translationX,存在平移过程,控件在x轴上偏移的像素值
- translationY,存在平移过程,空间在y轴上偏移的像素值
- left,控件左上角在父容器中横坐标的像素值
- right,控件左上角在父容器中纵坐标的像素值
- top,控件右下角在父容器中横坐标的像素值
- bottom,控件右下角在父容器中横坐标的像素值
- scrollX,控件在x轴上滑动的长度(px)
- scrollY,控件在y轴上滑动的长度(px)
存在如下关系如下:
x = left + translationX;
y = right = translationY;
scrollX = getScrollX;
scrollY = getScrollY;
TouchSlop
表示系统所能识别的最小滑动距离。通过以下方法获取:
ViewConfiguration.get(getContext()).getScaledTouchSlop()
VelocityTracker
用于跟踪手指在滑动过程中的速度,包括水平和竖直方向的速度。获取如下所示
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(motionEvent);
velocityTracker.computeCurrentVelocity(1000);
float x = velocityTracker.getXVelocity();
float y = velocityTracker.getYVelocity();
velocityTracker.clear();
velocityTracker.recycle();
GestureDetector
如果只是坚挺滑动相关,倾向于在onTouchEvent中实现,如果要监听双击这样的行为,就是用GestureDetector
Scroller的使用
用于实现View的弹性滑动,配合在自定义View中重写ComputeScroll方法来共同达到这一功能。比如自定义一个线性布局,调用smoothScrollBy方法实现平滑滚动,代码如下:
public class ScrollerLinearLayout extends LinearLayout {
public static final String TAG = "ScrollerLinearLayout";
private Scroller mScroller;
public ScrollerLinearLayout(Context context) {
super(context);
mScroller = new Scroller(context);
}
public ScrollerLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public void smoothScrollTo(int dstX,int dstY){
int dx = dstX - mScroller.getFinalX();
int dy = dstY - mScroller.getFinalY();
smoothScrollBy(dx,dy);
}
public void smoothScrollBy(int dx,int dy,int duration){
//上一次的最后位置作为本次重绘的开始位置,故调用getFinalX方法而不是getStartX方法
mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,dy,duration);
invalidate();
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
int currX = mScroller.getCurrX();
int currY = mScroller.getCurrY();
scrollTo(currX,currY);
postInvalidate();
}
super.computeScroll();
}
}
View的滑动
实现方式
使用scrollTo和scrollBy
scrollBy实际上是调用scrollTo方法,它实现了基于当前位置的相对滑动,而scrollTo实现了基于所传递的参数的绝对滑动。
滑动过程中,有两个属性值:mScrollX,mScrollY
通过getScrollX和getScrollY方法分别得到
mScrollX表示View左边缘到内容左边水平方向的距离
mScrollY表示View上边缘到内容上边竖直方向的距离
因此,scrollTo和scrollBy智能改变View的内容位置而不能改变View在布局中的位置。需要注意的是,使用scrollBy滑动,如果从左到右滑动,mScrollX为负值,从上往下滑动,mScrollY为负值。(前一个值减去后一个值)
使用动画
通过动画,我们能够让一个View进行平移,而平移就是一种滑动。通过操作View的translationX和translationY属性(因此View的平移,是在其父容器中移动),补间动画或属性动画都可以实现效果。
使用动画来做View的滑动需要注意的事项
补间动画是对View的映像做操作,它并不能真正改变View的位置参数,包括宽/高(如果需要View保留最后的动画状态,fillAfter置为true),因为View对应的位置信息(top,left,right,button)并没有改变,改变的只是translationX,translationY,x,y的值,而点击事件的位置根据View的位置信息来操作。对比属性动画,它是通过改变属性形成的动画,但是属性动画也存在不足,它只适用在Android 3.0以上。
改变布局参数
改变LayoutParams。
比如如果想让一个Button右移100px
- LayoutParams里让marginLeft参数值为100px即可。
- 在Button左边放置一个空的View,这个空View默认宽度为0,慢慢增加View的宽度,Button根据父容器的对齐方式,挤向另一边。
涉及到的API: ViewGroup.LayoutParams,ViewGroup.MarginLayoutParams
弹性滑动
将一次大的滑动分成若干次小的滑动并在一个时间段内完成。实现方式有:Scroller;Handler&postDelay;Thread&sleep。
使用Scroller
工作机制
当我们构造一个Scroller对象并且调用startScroll方法时,Scroller内部其实什么也没做,它只是保存了我们传递的参数。invalid方法会导致View重新绘制,即调用draw方法,draw方法里会调用computeScroll方法,这个方法需要我们去重写,在该方法里,调用scroller.computeScrollOffset方法,这个方法内部有时间因子(时间插值器),得到当前滑动的currX和currY,然后通过scrollTo方法实现滑动,接着又调用postInvalide方法进行第二次重绘。第二次重绘和第一次重绘过程一样。如此循环,直到滑动结束,computeScrollOffSet返回false。
通过动画
动画具有天然的弹性滑动
借鉴Scroller机制,通过ValueAnimator的onAnimationUpdate方法里,引入时间因子(Fraction)获取到每时每刻滑动的偏移量,再配合scrollTo/scrollBy,来完成也行。
使用延时策略
通过发送一系列延时的消息从而达到一种渐进式的效果。
具体实现方法:
- 使用Handler或View的postDelayed方法。
- while循环加sleep