Android中Scroller的使用及原理解析

1. Scroller的使用

1.1 构造Scroller

 //构造1
 scroller = new Scroller(context);
 //构造2
 scroller = new Scroller(context, new Interpolator() {
            @Override
            public float getInterpolation(float input) {
                return 0;
            }
        });

可以看到Scroller有2个构造器,其中第二个构造器需要传入Interpolator,它是一个插值器,决定了view在滑动中的效果。第一种构造会默认使用ViscousFluidInterpolator这个插值器。

1.2 重写View的computeScroll()

    @Override
    public void computeScroll() {
        super.computeScroll();
        //判断滑动是否完成,true为未完成,false未完成
        if (scroller.computeScrollOffset()){
            //将view移动到当前滑动都都位置
            scrollTo(scroller.getCurrX(),scroller.getCurrY());

        }
    }

1.3 开始滑动

//这里代表用时3000毫秒,从100,100的位置分别在X轴分别移动500个像素,负值代表向右,正值代表向左
scroller.startScroll(100,100,-500,-500,3000);
//重绘页面
invalidate();

以上就是Scroller的简单使用,需要注意的是Scroller移动的是View的内容而不是View本身,如一个LinearLayout里有2个Button,移动的是这2个Button而不是LinearLayout自己(原因在源码解析里)。

另外Scroller还有个快速滑过的方法:

//startX-起始x轴位置
//startY-起始Y轴位置
//velocityX-x轴初始速度
//velocityY-y轴初始速度
//minX-x轴方向最小值,scroller的滑动不会超过这个点
//maxX-x轴方向最打值,scroller的滑动不会超过这个点
//minY-y轴方向最小值,scroller的滑动不会超过这个点
//maxY-y轴方向最大值,scroller的滑动不会超过这个点
public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY) {}

2. Scroller的源码分析

从Scroller的使用可以看出,View的移动其实是在computeScroll()中通过自身的scrollTo()完成的。

2.1 先看下startScroll():

    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        //动画时常
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        //x轴起点
        mStartX = startX;
        //y轴起点
        mStartY = startY;
        //最终x轴的值
        mFinalX = startX + dx;
        //最终y轴的值
        mFinalY = startY + dy;
        mDeltaX = dx;
        mDeltaY = dy;
        mDurationReciprocal = 1.0f / (float) mDuration;
    }

可以看到startScroll只是记录了传入了参数,并没有做什么实际操作。

2.2 前面说过移动是通过scrollTo()完成的,那么看看它的源码

    public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }

这里可以看到scrollTo更新了mScrollX,和mScrollY的值,最终会触发重绘调用invalidate(),最终会调用到viewGroup的drawChild():

    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

最终调用到了view的3个参数的draw(),再继续看

  boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
          if (!drawingWithRenderNode) {
            computeScroll();
            sx = mScrollX;
            sy = mScrollY;
        }
         canvas.translate(mLeft - sx, mTop - sy);
  }

在draw()中调用了computeScroll(),然后把最新的mScrollX,mScrollY赋值给sx,sy,最后完成移动。

那么就是这样一个流程:
startScroll()->invalidate()->draw()->computeScroll()-scrollTo()->invalidate()->draw()->computeScroll()-scrollTo()...
并一直循环,直到scroller.computeScrollOffset()返回false,滑动完成。

3:OverScroller:

OverScroller和Scroller基本相同,不过它比Scroller更加完善,并且进行了一些扩展,主要添加了越界操作

3.1 看一下OverScroller扩展的方法

  • isOverScrolled():判断是否滑动出了边界

  • springBack(int startX, int startY, int minX, int maxX, int minY, int maxY):实现回弹效果,返回true代表控件不在指定范围内,false反之

  • fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY):这里比Scroller的fling的多2个参数,实现了回弹效果,overX决定了x轴可以滑出屏幕多远再弹回来,overY决定了y轴可以滑出屏幕多远再弹回来。(回弹效果的实现和springBack()调用的是同样的方法)

4:总结

  • OverScroller是Scroller的扩展,它实现了回弹效果,一般推荐使用OverScroller

  • 调用startScroll()确定起始位置以及偏移量,然后调用invalidate()让view绘制在起始位置

  • invalidate()最终会触发draw(),然后触发computeScroll()

  • 重写computeScroll(),通过OverScroller获取最新计算出的x,y值,然后再通过View自身的scrollTo()更新x,y值

  • 更新后再次invalidate(),以此重复,一直更新坐标,一直绘制到新的位置,完成滑动

  • 最后通过OverScroller的computeScrollOffset(),判断滑动是否完成,完成后就不再调用scrollTo(),最终完成整个滑动过程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容