杨子刚的博客


Android平滑滑动

2015-01-26

在ScrollView、Listiew中,单个手指在屏幕上快速划过后,控件会按照惯性继续滑动一段距离, 这个效果向用户提供了一个很好的体验。使用Scroller这个类,我们自己也可以在自定义的View 上面实现类似的效果。

Scroller实现起来很简单,看一下代码

  1 package com.ingphone.helloscroller;
  2 
  3 import android.content.Context;
  4 import android.graphics.Canvas;
  5 import android.graphics.Paint;
  6 import android.util.AttributeSet;
  7 import android.util.Log;
  8 import android.view.KeyEvent;
  9 import android.view.MotionEvent;
 10 import android.view.VelocityTracker;
 11 import android.view.View;
 12 import android.widget.Scroller;
 13 
 14 public class HelloScrollerView extends View {
 15     
 16     private Paint paint;
 17     private VelocityTracker velocityTracker;
 18     private Scroller scroller;
 19     private float lastX;
 20     private float currentX = 200;
 21 
 22     public HelloScrollerView(Context context) {
 23         super(context);
 24         scroller = new Scroller(context);
 25         // TODO Auto-generated constructor stub
 26     }
 27 
 28     public HelloScrollerView(Context context, AttributeSet attrs) {
 29         super(context, attrs);
 30         // TODO Auto-generated constructor stub
 31         scroller = new Scroller(context);
 32     }
 33 
 34     public HelloScrollerView(Context context, AttributeSet attrs,
 35             int defStyleAttr) {
 36         super(context, attrs, defStyleAttr);
 37         scroller = new Scroller(context);
 38         // TODO Auto-generated constructor stub
 39     }
 40 
 41 // public HelloScrollerView(Context context, AttributeSet attrs,
 42 //         int defStyleAttr, int defStyleRes) {
 43 //     super(context, attrs, defStyleAttr, defStyleRes);
 44 //     // TODO Auto-generated constructor stub
 45 // }
 46     
 47     @Override
 48     public void onDraw(Canvas canvas) {
 49         if (paint == null) {
 50             paint = new Paint();
 51         }
 52         canvas.drawText("测试", currentX, 50, paint);
 53     }
 54     
 55     public boolean onKeyDown (int keyCode, KeyEvent event) {
 56         Log.e("KeyEventView", " " + keyCode);
 57 //     textView.setText(KeyEvent.keyCodeToString(keyCode) + "");
 58         return true;
 59     }
 60     
 61     @Override
 62     public boolean onKeyUp (int keyCode, KeyEvent event) {
 63         Log.e("KeyEventView", " " + keyCode);
 64 //     textView.setText(KeyEvent.keyCodeToString(keyCode) + "");
 65         return true;
 66     }
 67     
 68       @Override
 69         public boolean onTouchEvent(MotionEvent ev)
 70         {
 71             super.onTouchEvent(ev);
 72 
 73             if (velocityTracker == null)
 74             {
 75                 velocityTracker = VelocityTracker.obtain();
 76             }
 77             velocityTracker.addMovement(ev);
 78 
 79             switch (ev.getAction())
 80             {
 81                 case MotionEvent.ACTION_DOWN:
 82                     if (!scroller.isFinished())
 83                     {
 84                         scroller.abortAnimation();
 85                     }
 86                     lastX = ev.getX();
 87                     break;
 88                 case MotionEvent.ACTION_MOVE:
 89                     currentX =  (currentX + (ev.getX() - lastX));
 90                     lastX = ev.getX();
 91                     break;
 92                 case MotionEvent.ACTION_UP:
 93                     velocityTracker.computeCurrentVelocity(1000);
 94                     scroller.fling((int) currentX, 0, (int) velocityTracker.getXVelocity(), 0, 0, 99999999, 0, 0);
 95                     velocityTracker.recycle();
 96                     velocityTracker = null;
 97                     if (!scroller.computeScrollOffset())
 98                     {
 99                     }
100                     break;
101             }
102             invalidate();
103             return true;
104         }
105 
106         @Override
107         public void computeScroll()
108         {
109             if (scroller.computeScrollOffset())
110             {
111                 currentX = scroller.getCurrX();
112                 invalidate();
113             }
114             else
115             {
116             }
117         }
118 }

当手指在屏幕上移动时,将移动信息传递给VelocityTracker(77行),当手指抬起时,使VelocityTracker计算手指移动的速度(93行),然后将手指在水平方向移动的速度做为参数传递给Scroller.fling(94行),Scroller的fling函数用来开始一个滑动减速计算。在每一次手指移动时,都要调用View的invalidate()函数(102行),系统之后会调用View的onDraw函数来对界面进行重绘。在重绘的时候,View会调用自己的computeScroll()函数,在这个函数中,我们向Scroller询问滑动是否还在进行(109行),如果正在滑动,则获取当前的X值,并调用invalidate()进行再次重绘。

public void fling (int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)函数是关键,fling的意思是猛甩,意即手指在屏幕上猛烈滑动并抬起这个动作。需要将起始点、速度、滑动的最短距离、最长距离作为参数传递进去。