Scroller类的使用总结

Scroll类之所以不好理解是因为没有搞清楚View的绘制流程。

1)简单来讲 viewgroup重绘时依次会调用  dispatchDraw -- drawChild --child.computeScroll()。

computeScroll是一个空函数,可以在里面写代码。

2)Scroller类一些api:

mScroller.getCurrX() //获取mScroller当前水平滚动的位置
mScroller.getCurrY() //获取mScroller当前竖直滚动的位置
mScroller.getFinalX() //获取mScroller最终停止的水平位置
mScroller.getFinalY() //获取mScroller最终停止的竖直位置
mScroller.setFinalX(int newX) //设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置
mScroller.setFinalY(int newY) //设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置

//滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间
mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默认完成时间250ms
mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)

mScroller.computeScrollOffset() //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。

其中非常重要的startScroll()和computeScrollOffset()函数。

startScroll函数的功能是设置一些值,最重要的值是设置了一个滚动开始时间和持续时间,其源码如下:

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

computeScrollOffset()是用来计算当前时间点理论上能够滚到的x,y坐标。

如果滚动持续时间到了则返回false,否则返回true并计算坐标。mCurrX和mCurrY得到更新。源码如下:

    public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }

        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:
                float x = 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:
                final float t = (float) timePassed / mDuration;
                final int index = (int) (NB_SAMPLES * t);
                float distanceCoef = 1.f;
                float velocityCoef = 0.f;
                if (index < NB_SAMPLES) {
                    final float t_inf = (float) index / NB_SAMPLES;
                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
                    final float d_inf = SPLINE_POSITION[index];
                    final float d_sup = SPLINE_POSITION[index + 1];
                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
                }

                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
                
                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
                // Pin to mMinX <= mCurrX <= mMaxX
                mCurrX = Math.min(mCurrX, mMaxX);
                mCurrX = Math.max(mCurrX, mMinX);
                
                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
                // 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;
    }

3)通过1、2我们可以知道如果要实现平滑滚动则需要在computeScroll函数里调用computeScrollOffset()来更新Scroller里面的mCurrX 和mCurrY,然后利用

scrollTo(mCurrX, mCurrY) 来让View的x、y坐标滚动到指定位置。此时并不一定会导致view重绘,手动调用postInvalidate()自上而下重绘view树。

注意scrollTo只会导致该view的内容发生偏移,并不会导致该view的位置偏移。比如对Button进行scrollTo 会导致Button里面的text发生偏移,而Button的位置不会发生偏移。

4) 有了上面的分析后下面是一段小实例,单击按钮后,按钮会慢慢下移。

CustomView.java:

public class CustomView extends LinearLayout {

    private Scroller mScroller = null;
    private Context mContext;
    
    public CustomView(Context context) {
        super(context);
        mContext = context;
        init();
    }    
    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }
    
    private void init() {
        if (mScroller == null) {
            mScroller = new Scroller(mContext);        
        }            
        this.setOrientation(VERTICAL);
        Button btn = new Button(mContext);
        btn.setText("按钮");
        LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
        this.addView(btn, params);    
        this.setBackgroundColor(Color.YELLOW);
    }
        
    //调用此方法设置滚动到目标位置    
    public void smoothScrollTo(int fx, int fy) {
        int dx = fx - mScroller.getFinalX();
        int dy = fy - mScroller.getFinalY();
        smoothScrollBy(dx, dy);
    }
    
    //调用此方法设置滚动的相对偏移
    public void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, 10000);
        invalidate();
    }
    
    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
    
        //查看是否滚动结束了,true表示没有结束
        if (mScroller.computeScrollOffset()) {
            //调用view的滚动函数,进行实际滚动。
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //只有postInvalidate了才能循环刷新。
            postInvalidate();
        }    
        super.computeScroll();
    }
}

MainActivity.java:

public class MainActivity extends Activity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        final CustomView view = new CustomView(this);
        setContentView(view);    
     //给button添加onClick监听 view.getChildAt(
0).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub view.smoothScrollBy(0, -100); } }); } }

参考网页:http://www.cnblogs.com/PDW-Android/p/3653253.html

http://blog.csdn.net/gemmem/article/details/7321910

http://www.open-open.com/lib/view/open1328834050046.html

原文地址:https://www.cnblogs.com/wliangde/p/3654948.html