android_Scroller 弹性滑动源码分析

原创 2016年08月30日 14:31:21

1、scrollTo/scrollBy

总所周知,使用 View 的 scrollTo 或者 scrollBy 方法实现 View 内容的滑动是一瞬间完成的,毫无过度可言,那么 Android 提供 Scroller 就可以解决这个问题,让 View 内容的滑动可以在一段时间内完成。

mImageView.scrollTo(100,100);

上面的示例代码表示 mImageView 的内容向左上角移动 100px,上面说过了,这种方式是一瞬间完成的,很坚硬。

为了解决这个问题,我们引入的 Scroller 类,下面看看如何使用这个类实现弹性滑动

2、Scroller 的实现原理

  • 准备工作:使用 Scroller#startScroll(…) 方法确定 View 内容的位置和需要滑动的距离,注意这个方法只是对 Scroller 对象的中一些属性进行赋值(具体看下下面源码部分),并没有产生任何的滑动操作。

  • 准备工作完成之后,该如何利用 Scroller 实现弹性滑动

    通过调用 View#invalidate() 方法去触发 View 的重绘操作,因为 View#draw 方法中会回调 computeScroll() 这个方法,而它是个空方法,我们只要在 View 中复写这个方法,就可以在每次 View 重绘制时获取当前 View 内容需要滑动的距离,然后调用 scrollTo 进行滑动。
    
  • 现在知道是通过 View#invalidate() 方法可以触发 View 进行重绘,那么如何在重绘的回调中取计算当前 View 内容需要滑动的距离呢?

    • 因为 View#computeScroll 方法会在 View#invalidate() 中回调,那么我们多次调用 invalidate 方法就可以多次回调了,并且在每一次回调中取设置当前 View 需要滑动到指定的位置。

    • 如何计算 View 需要滑动的距离呢? 在 Scroller#onComputeScrollOffset() 方法内部会根据时间去计算当前 View 内容的 scrollX 和 scrollY 的值,因此在 View#computeScroll() 方法中去对调用 onComputeScrollOffset 进行计算即可,然后获取计算后的值,然后调用 scrollTo 进行滑动即可。

3、示例代码

我们在第 2 点中说了很多,可能会有点迷糊,下面以实际代码来演示可能会更加清晰一些。下面是第 2 点实现原理的具体代码实现:

//定义Scroller对象
Scroller mScroller = new Scroller(mContext);

/**
* @des 滑动到某个位置
* @param dx 表示x方向需要滑动的大小
* @param dy 表示y方向需要滑动的大小
*/
private void smoothScrollBy(int dx, int dy) {
    int scrollX = getScrollX();
    int scrollY = getScrollY();
    //这个方法是在初始化坐标(scrollX,scrollY)水平方向滑动dx像素,竖直方向滑动dy个像素,滑动时间为500ms。
    mScroller.startScroll(scrollX, scrollY, dx, dy, 500);
    //该方法标记该 View 会进行重绘,并且回调 computeScroll() 方法。
    invalidate();
}

//重写View的computeScroll()方法,该方法会在View绘制的时候调用
@Override
public void computeScroll() {
    //计算当前的 scrollX 和 scrollY 的值,并且根据返回值判断滑动是否结束。
    if (mScroller.computeScrollOffset()) {
        //滚动结束
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//计算后的 scrollX 和 scrollY 的值
        postInvalidate();//每次滑动之后,都要重绘一遍
    }
}

4、源码分析

4.1、弹性滑动入口 startScroll(int startX, int startY, int dx, int dy)

/**
 * Start scrolling by providing a starting point, the distance to travel,
 * and the duration of the scroll.
 * 
 * @param startX Starting horizontal scroll offset in pixels. Positive
 *        numbers will scroll the content to the left.
 * @param startY Starting vertical scroll offset in pixels. Positive numbers
 *        will scroll the content up.
 * @param dx Horizontal distance to travel. Positive numbers will scroll the
 *        content to the left.
 * @param dy Vertical distance to travel. Positive numbers will scroll the
 *        content up.
 * @param duration Duration of the scroll in milliseconds.
 */
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;
    //滑动时间的倒数
    mDurationReciprocal = 1.0f / (float) mDuration;
}

4.2、scrollTo 是在 computeScroll() 回调中调用的

在调用 Scroller#startScroll 方法之后,需要再调用invalidate方法,这个方法会让View进行重新绘制,也就
是说会重新的调用 draw() 方法,查阅View#draw()方法可以看到,该方法中还调用 computeScroll 方法,并且该
方法是一个空方法,我们重写该方法,在这个方法中我们捕获每次 View 重绘的时机,然后再进行 scrollTo 滑动即可。

  • View 中的 draw 方法调用 computeScroll 的代码片段
...
if (!drawingWithRenderNode) {
    computeScroll();//绘制时,调用该方法
    sx = mScrollX;
    sy = mScrollY;
}
...

4.3、View中的computeScroll方法,它是一个空方法,源码如下,它的源码大概的意思如下:该方法用于在弹性滑动时更新 View 的 scrollX 和 scrollY 的值。

/**
 * Called by a parent to request that a child update its values for mScrollX
 * and mScrollY if necessary. This will typically be done if the child is
 * animating a scroll using a {@link android.widget.Scroller Scroller}
 * object.
 */
public void computeScroll() {
}

然而在每次调用 srcollTo 进行滑动之后,我们总得知道什么时候滑动到终点啊和下次要滑动的位置?这个时候就得了解另一个方法Scroller#computeScrollOffset()

4.4、computeScrollOffset 分析

从方法解释中可以大概的知道:
1.方法返回 true 表示滑动还没结束,返回 false 代表滑动已经结束,如果没有结束,获取计算后的 scrollX 和 scrllY 的值进行再次的滑动。
2.该方法可以计算滑动后的位置 scrollX 和 scrollY 的值,也就是 mCurrX 和 mCurrY 的值,供下次 View#scroll(scrollX,scrollY) 使用。

/**
* Call this when you want to know the new location.  If it returns true,
* the animation is not yet finished.
* 
*/ 
public boolean computeScrollOffset() {
   if (mFinished) {//使用 mFinished 标记滑动是否结束
       return false;
   }

   int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

   if (timePassed < mDuration) {
       switch (mMode) {
       case SCROLL_MODE:
           //timePassed * mDurationReciprocal
           //timePassed:当前时间 - 开始时间
           //mDurationRecipracal:1f/mDuration
           //根据流逝的时间计算出一个百分比值
           final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
           //更新 mCurrX 和 mCurrY 的值
           mCurrX = mStartX + Math.round(x * mDeltaX);//四舍五入操作
           mCurrY = mStartY + Math.round(x * mDeltaY);
           break;
       case FLING_MODE://这种方式暂时不分析。
            ...
            break;
        }
    }
    else {//滑动时间到
        mCurrX = mFinalX;
        mCurrY = mFinalY;
        mFinished = true;
    }
    return true;
}

5、总结

Scroller 是用于解决 View#scrollTo/scrollBy() 滑动效果问题,借助它可以实现弹性滑动效果。实现原理简单来说就是 View 内容在可以在一段时间内完成一段距离的滑动,在这段时间内不断地通过回调去调用 View#scrollTo 方法,从而实现弹性滑动效果。

以上是个人对 Scroller 的学习笔记,有任何不对之处望大家批评指正

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android开发——弹性滑动的两种实现方式

0. 前言  我们在Android开发——View滑动的三种实现方式中学习了如何进行View滑动,在第一种方法,利用ScrollBy和ScrollTo进行滑动时,滑动效果是瞬间完成的,为了更好的用户体...
  • SEU_Calvin
  • SEU_Calvin
  • 2016年09月14日 15:57
  • 9579

View弹性滑动的几种实现方法

Scroller类实现简单 使用的ScrollTo方法 使用动画比如让其在1000内滑动过去 或者利用动画的回调接口 根据动画执行的百分比 来进行滑动效果的滑动进度控制 很聪明的做法 ...
  • u012760183
  • u012760183
  • 2016年05月16日 07:44
  • 145

View的滑动之瞬间滑动与弹性滑动

View的滑动之瞬间滑动与弹性滑动
  • qq_24530405
  • qq_24530405
  • 2016年01月19日 15:53
  • 1224

Android中View的弹性滑动——Android开发艺术探索笔记

原文链接 http://sparkyuan.me/ 转载请注明出处介绍弹性滑动也就是渐进式滑动,实现弹性滑动的方法有很多,但是他们都有一个共同的思想:将一次大的滑动分成若干次小的滑动并在一段时间内完成...
  • L664675249
  • L664675249
  • 2016年02月24日 17:05
  • 2690

Android弹性滑动

1、View使用Scroller滑动 /** * 在scroller.startScroll()方法中,会将scrollX和deltaX相加存入mFinalX中 * invalidate()方法会...
  • Qyuewei
  • Qyuewei
  • 2015年12月18日 11:39
  • 296

Android Scroller类详解--实现View内容弹性滑动

概述Scroller类封装了滚动,我们可以使用Scroller 类来收集滑动动画过程中的数据,例如为了响应快速滚动动画。当View的位置发生变化的时候,Scroller会自动追踪scrollX 和sc...
  • hpk1994
  • hpk1994
  • 2016年04月22日 22:26
  • 546

android listview 的弹性滑动

弹性滑动的效果比较好看。但是 在2.2以下版本中,android 本身没有实现,想要实现这中效果要自己去写 前几天在iteye上看见了一个博客给我很大的启示,于是自己动手实验了一下感觉还可以。在...
  • u014702999
  • u014702999
  • 2014年12月18日 16:50
  • 855

使用动画完成弹性滑动

使用动画完成弹性滑动 动画本身就是一种渐进的过程,因此通过它来实现的滑动天然就具有弹性的效果。 让一个View在100ms内向左移动100pxObjectAnimatior.ofFloat(tar...
  • k316378085
  • k316378085
  • 2016年05月12日 21:14
  • 162

使用Scroller实现弹性滑动

scrollerTo、scrollBy View内部为了实现滑动提供了这两个方法,但是使用这两个方法滑动的效果是瞬间的不够平滑,如何实现View的弹性滑动呢?这正是本博文讨论的主题。另外这两个函数滑动...
  • haohengyuan
  • haohengyuan
  • 2016年08月22日 16:29
  • 2458

使用Scroller实现View的弹性滑动

View有scrollTo、scrollBy方法,但是滑动是瞬时的,比较僵硬。可以通过Scroller来实现View的弹性滑动。 ScrollView就有smoothScrollTo和smooth...
  • whsdu929
  • whsdu929
  • 2016年08月08日 20:07
  • 1067
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android_Scroller 弹性滑动源码分析
举报原因:
原因补充:

(最多只允许输入30个字)