Android使用Scroller实现弹性滑动效果

本文实例为大家分享了Android使用Scroller实现弹性滑动展示的具体代码,供大家参考,具体内容如下

scrollTo、scrollBy

View内部为了实现滑动提供了这两个方法,但是使用这两个方法滑动的效果是瞬间的不够平滑,如何实现View的弹性滑动呢?这正是本博文讨论的主题。另外这两个函数滑动的是View的内容不是View本身。比如对于普通View好比TextView其内容就是文本,ImageView的内容则是drawable对象,采用这两种方法滑动的时候其实分别滑动的是文本及drawable对象,对于ViewGroup采用这两种方法滑动的时候则是对其子元素的滑动。所以想要使用scrollTo、scrollBy方法实现拖动View(指的是普通的View不包含ViewGroup)的效果必须在View外面在包一层ViewGroup。

Scroller类

上面提到使用scrollTo、scrollBy来滑动View的时候是很生硬得滑过去的,不够平滑,自然用户体验也不好,因此我们要实现一个弹性的滑动。如何实现弹性滑动呢?方法有很多,但思想都是一致的,即将实现一段距离的滑动分成多次来进行,每一次滑动一小段,渐近式的滑动。本文只是介绍其中的一种即使用Scroller实现弹性滑动。以下结合实例看看Scroller是如何实现平滑滑动的呢 ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class SmoothScrollView extends LinearLayout{
  
  Scroller mScroller ;
  int startX;
  int startY;
public SmoothScrollView(Context context, AttributeSet attrs) {
  super(context, attrs);
  //创建Scroller实例
  mScroller = new Scroller(context);
  }
  
  
public void smoothScroll(int dx,int dy,int duration){
  //获取滑动起点坐标
  startX = getScrollX();
  startY = getScrollY();
  //设置滑动参数
  mScroller.startScroll(startX,startY,dx,dy,duration);
  //重新绘制View
  invalidate();
  }
  
@Override
public void computeScroll() {
  // TODO Auto-generated method stub
  super.computeScroll();
  boolean flag = mScroller.computeScrollOffset();
  //递归终止条件:滑动结束
  if(flag == false){
    return;
  }else{
  //mScroller.getCurrX(),mScroller.getCurrY()记录的是此刻要滑动达到的目标坐标
  scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
  }
  //递归调用
  invalidate();//或者postInvalidate()
  }
}

 看到第9行,首先在SmoothScrollView内部创建一个Scroller对象,第13行的smoothScroll方法是实现SmoothScrollView的平滑滑动,可以看到实现平滑滑动首先调用第18行Scroller的startScroll方法来设置滑动参数,下文会分析这个方法,这里先放一放。然后在第20行调用invalidate方法,这个方法会导致SmoothScrollView重绘,从而调用draw方法之后又会调用computeScroll方法,在第24行可以看到这里重写了computeScroll方法,因此调用invalidate方法最终会导致computeScroll方法被调用。第27~29行调用Scroller的computeScrollOffeset方法并判断是否滑动结束,computeScrollOffset是如何判断滑动结束的呢?这里也先放一放下文在分析。如果滑动未结束,执行第33行调用scrollTo滑动SmoothScrollView至此刻目的坐标,然后递归调用invalidate方法。

以下是对Scroller几个方法的分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    mMode = SCROLL_MODE;
    mFinished = false;
    mDuration = duration;
  //记录开始滑动的时间
    mStartTime = AnimationUtils.currentAnimationTimeMillis();
  //滑动起始横坐标
    mStartX = startX;
  //滑动起始纵坐标
    mStartY = startY;
  //滑动结束横坐标
    mFinalX = startX + dx;
  //滑动结束纵坐标
    mFinalY = startY + dy;
  //横向滑动偏移量
    mDeltaX = dx;
  //纵向滑动偏移量
    mDeltaY = dy;
  //mDuration表示的是整个滑动持续的时间
    mDurationReciprocal = 1.0f / (float) mDuration;
  }

从以上代码可以看到startScroll方法其实根本没有滑动View只是对滑动参数进行设置。往下再来看看computeScrollOffset方法,computeScrollOffset返回true则表示滑动还没结束返回false表示滑动结束,它的实现如下:
[java] view plain copy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public boolean computeScrollOffset() {
    if (mFinished) {
      return false;
    }
  
    int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
   //判断此刻是否在有效滚动周期内
    if (timePassed < mDuration) {
      switch (mMode) {
      case SCROLL_MODE:
  //当前时刻滑动偏移量所占份额
        float x = (float)timePassed * mDurationReciprocal;
    
        if (mInterpolator == null)
          x = viscousFluid(x); 
        else
          x = mInterpolator.getInterpolation(x);
        //获取当前时刻要滚动到的位置
        mCurrX = mStartX + Math.round(x * mDeltaX);
        mCurrY = mStartY + Math.round(x * mDeltaY);
        break;
      case FLING_MODE:
        float timePassedSeconds = timePassed / 1000.0f;
        float distance = (mVelocity * timePassedSeconds)
            - (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
          
        mCurrX = mStartX + Math.round(distance * mCoeffX);
        // Pin to mMinX <= mCurrX <= mMaxX
        mCurrX = Math.min(mCurrX, mMaxX);
        mCurrX = Math.max(mCurrX, mMinX);
          
        mCurrY = mStartY + Math.round(distance * mCoeffY);
        // Pin to mMinY <= mCurrY <= mMaxY
        mCurrY = Math.min(mCurrY, mMaxY);
        mCurrY = Math.max(mCurrY, mMinY);
  
        if (mCurrX == mFinalX && mCurrY == mFinalY) {
          mFinished = true;
        }
  
        break;
      }
    }
    else {
      mCurrX = mFinalX;
      mCurrY = mFinalY;
      mFinished = true;
    }
    return true;
  }

看到这我们应该明白SmoothScrollView是如何实现让自己平滑滑动的呢?实际上正真让SmoothScrollView产生平滑滑动的并非是Scroller而是SmoothScrollView自己,是SmoothScrollView自己多次调用了自己的scrollerTo方法并且每次滑动一小步从而实现平滑滑动,而Scroller类干的事只是辅助SmoothScrollView计算每一次小滑动要到达的目标坐标,而实现多次调用了自己的scrollerTo方法这里没有使用循环体则是利用程序设计技巧“递归调用”invalidate方法达到多次调用scrollTo方法的目的从而实现平滑滑动。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

posted @ 2019-06-20 18:24  天涯海角路  阅读(728)  评论(0)    收藏  举报