实现弹性滑动的方法很多,但都是一个思想:就是把一个大的滑动分成若干小的滑动并在一定的时间段内完成。下面介绍几种实现的方法:
(1)使用Scroller
这种方法在《View的事件体系》(一)中已经贴出了源码,这里再分析一下:
Scroller scroller =new Scroller(context);
private void smoothScrollTo(int x,int y){
int scrollX = getScrollX();
int delta = x – scrollX;
//1000秒内滑向x,慢慢移动
scroller.startScroll(scrollX,0,delate,0,1000);
invalidate();
}
@Override
public void computeScroll(){
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(),scroller.getCurrY());
postInvalidate();
}
}
Scroller和View是两个不同的个体,代码的思路就是让Scroller先移动,然后让View跟着Scroller的脚步进行移动。代码中我们构造先一个Scroller并在smoothScrollTo中调用他的startScroll方法,startScroll方法的作用就是保存这几个参数,从左到右分别是滑动的起始X,Y坐标,终点X,Y坐标和整个滑动过程所需要的时间。真正完成滑动的是invalidate(),invalidate会让View重新绘制,在View的draw方法中又会去调用computeScroll方法,在View中computeScroll方法是一个空实现,在上面的代码中已经给出了实现。在computeScroll中,我们看到mScroller.computeScrollOffset(),他的作用不仅是判断滚动是否结束,而且根据时间的流逝的百分比计算出scrollX和scrollY改变的百分比,还在里面对Scroller.mCurrX和Scroller.mCurrY进行了赋值,也就是在滚动的过程中,实时的更新CurrX、CurrY的坐标。scrollTo(scroller.getCurrX(),scroller.getCurrY())就是让View跟着scroller改变位置。postInvalidate方法会进行第二次重绘,过程和第一次一样,如此反复,直到滑动结束。
他不断地让View重绘,每一次重绘距滑动起始时间都会有一个时间间隔,通过这个时间间隔,Scroller得到View下次滑动到的位置,继而View通过scrollTo完成滑动。这种设计简直绝妙!
(2)使用动画
final int startX = 0;
final int deltaX = 100;
ValueAnimator animator=ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListener(newAnimatorUpdateListener(){
public void onAnimationUpdate(ValueAnimator animator){
float fraction =animator.getAnimatedFraction();//fraction,分数
button.scrollTo(stratX+(int)(deltaX*fraction),0);
}
}) ;
animator.start;
在上面的代码中只是在1000ms内完成了整个动画过程。利用这个特性,我们就可以在动画的每一帧到来的时候获取动画完成的比例,进而根据这个比例计算出当前View所要滑动的距离。我们完全可以在onAnimationUpdate中加入其它想要的操作。
(3)延时策略
可以使用Handler或者View的postDelayed方法,也可以使用线程的sleep方法。对于前者我们利用postDelayed发送延时消息,在消息中让View滑动,接连不断的发送这种消息就可以实现弹性滑动的效果。对于后者来说通过在while循环中不断滑动View和sleep,就可以实现。下面以Handler为例:
private intmCount = 0;
intMESSAGE_SCROLL_TO = 0;
int FRAME_COUNT= 30;
int DELAYED_TIME= 33;
private Handlerhandler = new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case MESSAGE_SCROLL_TO:{
mCount++;
if(mCount<= FRAME_COUNT){
float fraction = mCount/(float)FRAME_COUNT
int scrollX = (int)(fraction*100);
button.scrollTo(scrollX,0);
handler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,DELAYED_TIME);
}break;
}
default:
break;
}
};
};
介绍这几种方法时更侧重思想,但结合前几篇讲解MotionEvent等关键概念,以及View的工作原理系列博文,便能进行进行灵活的扩展了。