Scroller滚动流程分析及一个scroller的妙用

最近在github看到一个开源项目——下拉刷新上拉加载listview组件。

地址:https://github.com/JosephPeng/XListView-Android

学习下来,我发现了一个特别有趣的东西,应该当时我在研究这个代码是给我造成成吨伤害的就是这个——Scroller。之前也有了解过这个东东,也用过。我实在没想到这个能对我造成困扰。特此记录下来,已做留念。哈哈

好了,不说废话了。代码早已饥渴难耐。有看过android源代码知道view.java里面有两个方法:scrollTo()和scrollBy()方法,从名字我们可以知道滚动view。

public void scrollTo(int x, int y) {

       if (mScrollX != x || mScrollY != y) {

           int oldX = mScrollX;          

           int oldY = mScrollY;

           mScrollX = x;                   //更新mScrollX和mScrollY

           mScrollY = y;

           onScrollChanged(mScrollX, mScrollY, oldX, oldY);

           if (!awakenScrollBars()) {

                invalidate();

           }

       }

    }

public void scrollBy(int x, int y) {

       scrollTo(mScrollX + x, mScrollY + y);

    }

 

 

这里说明下:mScrollX和mScrollY是view类中的两个成员变量,用来保存当前view的偏移量。也就是我们让view偏移位置就可以调用这两个方法。这样有什么问题呢?我们假设要从(0,0)坐标滚动(600,800)坐标。然后我们调用scrollTo(600,800),不要眨眼,你看到就是该view刷的一下就飞到了你所要的位置。这样的用户体验不能再差了。我们要view慢下来,不要急,在规定的时间到达就行了。这时Scroller出现了,对view老兄说:“hey,兄弟,我来帮你”。scroller开启的功能startScroll()

public void startScroll(int startX, intstartY, 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;

       // This controls the viscous fluid effect (how much of it)

       mViscousFluidScale = 8.0f;

       // must be set to 1.0 (used in viscousFluid())

       mViscousFluidNormalize = 1.0f;

        mViscousFluidNormalize = 1.0f /viscousFluid(1.0f);

    }

但我们很快发现被Scroller忽悠了,scroller中startScroll好像没送view到指定地点。这其中究竟有什么阴谋,它的背后又想隐藏什么?startScroll这个方法只是保存了一些变量而已,其他什么都没做。其实它还有帮手,一般会在这后面调用invalidate();这里大家就清楚了view将重绘dispatchDraw,这个函数过长,这里不贴源代码,在diapatchDraw里接着调用drawChild()函数。drawChild里面会调用一个computeScroll方法。但发现view类中此方法是个空方法,也就是需要我们自己重写。我们就是在这个方法控制view的偏移。

@Override

         publicvoid computeScroll() {

                   if(mScroller.computeScrollOffset()) {

                            if(mScrollBack == SCROLLBACK_HEADER) {

                                     /**

                                      * computeScrollOfferset此方法就是产生动画

                                      * 而是通过getCurrentY参数设置可见headerview高度

                                      */

                                     mHeaderView.setVisiableHeight(mScroller.getCurrY());

                            }else {

                                     /**

                                      * 同上 只需要一个从bottomMargin~0不断变小的值

                                      */

                                     Log.i("computeScroll","scrollerX="+mScroller.getCurrX()+"scrollerY="+mScroller.getCurrY());

                                     mFooterView.setBottomMargin(mScroller.getCurrY());

                            }

                            postInvalidate();

                            invokeOnScrolling();

                   }

                  

                   super.computeScroll();

         }

 

 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);

               

                break;

           }

       }

       else {

           mCurrX = mFinalX;

           mCurrY = mFinalY;

           mFinished = true;

       }

       return true;

    }

我们可以看出此函数就是通过当前动画时间计算当前偏移值currX和currY。返回值滚动是否结束。所以我们在computeScroll调用scrollTo(getCurrX(),getCurrY())实现平滑滚动。

至此view滚动流程原理分析完了。但跟我说的有毛线关系啊。对啊,说了这么还没到高潮部分。接下来讲我很感兴趣的地方。

这个感兴趣的地方是这里的滚动压根没用到上面所说的scrollTo()和scrollBy().我们知道了Scroller类只是保存了一些变量。那我们也可以利用这些变量做其他事,比如说利用currY设置listview的header的高度

public void computeScroll() {

                   if(mScroller.computeScrollOffset()) {

                            if(mScrollBack == SCROLLBACK_HEADER) {

                                     /**

                                      * 并不是真正意义上的滚动没用scrolltoor scrollby  一直在这犯错了。。。

                                      * 而是通过getCurrentY参数设置可见headerview高度

                                      */

                                     mHeaderView.setVisiableHeight(mScroller.getCurrY());

                            }else {

                                     /**

                                      * 同上 只需要一个从bottomMargin~0不断变小的值

                                      */

                                     Log.i("computeScroll","scrollerX="+mScroller.getCurrX()+"scrollerY="+mScroller.getCurrY());

                                     mFooterView.setBottomMargin(mScroller.getCurrY());

                            }

                            postInvalidate();

                            invokeOnScrolling();

                   }

                  

                   super.computeScroll();

         }

这样就不会对着下面代码看着官方文档都流泪了,什么Y正值向上滚动,负值向下滚动,都是浮云。

mScroller.startScroll(0, bottomMargin, 0,-bottomMargin,

SCROLL_DURATION);

mScroller.startScroll(0, height, 0,finalHeight - height,

SCROLL_DURATION);


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值