Scroller简介

如果想把一个View移动到(x, y)位置,我们通过调用scrollTo() 或者scrollBy()方法就能实现,但是在实际中我们往往希望View平滑移动,此时Scroller就派上了用场。


Scroller.java

1. 

public class Scroller  {
	    private int mStartX;//起始x坐标
	    private int mStartY;//起始y坐标
	    private int mCurrX;//当前x坐标
	    private int mCurrY;//当前y坐标
	    private boolean mFinished;//是否结束
	    
	    /**
	     * Returns the current X offset in the scroll. 
	     * 
	     * @return The new X offset as an absolute distance from the origin.
	     */
	    public final int getCurrX() {
	        return mCurrX;
	    }
	    
	    /**
	     * Returns the current Y offset in the scroll. 
	     * 
	     * @return The new Y offset as an absolute distance from the origin.
	     */
	    public final int getCurrY() {
	        return mCurrY;
	    }
	    
	    /**
	     * 
	     * Returns whether the scroller has finished scrolling.
	     * 
	     * @return True if the scroller has finished scrolling, false otherwise.
	     */
	    public final boolean isFinished() {
	        return mFinished;
	    }
	    
	    /**
	     * Force the finished field to a particular value.
	     *  强制结束
	     * @param finished The new finished value.
	     */
	    public final void forceFinished(boolean finished) {
	        mFinished = finished;
	    }
	    
	    /**
	     * Start scrolling by providing a starting point and the distance to travel.
	     * The scroll will use the default value of 250 milliseconds for the
	     * duration.
	     * 
	     * @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.
	     */
	    public void startScroll(int startX, int startY, int dx, int dy) {
	        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
	    }
	    
	    /**
	     * Start scrolling by providing a starting point and the distance to travel.
	     * 开始平滑移动,由(startX, startY)位置在duration时间内移动(dx, dy)个单位
	     * @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;
	    }
	    
	    /**
	     * Call this when you want to know the new location.  If it returns true,
	     * the animation is not yet finished.  loc will be altered to provide the
	     * new location.
	     * 计算当前平滑移动的位置,保存在mCurrX, mCurrY中
	     * 返回结果 true:平滑移动未结束 false:平滑移动结束
	     */ 
	    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);
	                final float t_inf = (float) index / NB_SAMPLES;
	                final float t_sup = (float) (index + 1) / NB_SAMPLES;
	                final float d_inf = SPLINE[index];
	                final float d_sup = SPLINE[index + 1];
	                final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
	                
	                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;
	    }


Scroller会帮我们计算duration时间段内任意时刻布局将要被移动到的x,y位置,比较重要的两个方法:

public void startScroll(int startX, int startY, int dx, int dy, int duration)

开始平滑移动,从(startX, startY)位置在duration时间内移动(dx, dy)个单位

public boolean computeScrollOffset()
计算当前时刻布局移动后的x,y位置,保存在mCurrX, mCurrY中,我们只有直接使用mCurrX, mCurrY即可。

2. 

还有一个非常重要的方法,

View.java

/**
     * 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() {
    }

此方法用来移动当前布局。


ViewGroup.java源码中

 /**
     * {@inheritDoc}
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        final int count = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;
        ....
        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        } else {
            for (int i = 0; i < count; i++) {
                final View child = children[getChildDrawingOrder(count, i)];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        }
        .....


/**
     * Draw one child of this View Group. This method is responsible for getting
     * the canvas in the right state. This includes clipping, translating so
     * that the child's scrolled origin is at 0, 0, and applying any animation
     * transformations.
     *
     * @param canvas The canvas on which to draw the child
     * @param child Who to draw
     * @param drawingTime The time at which draw is occuring
     * @return True if an invalidate() was issued
     */
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        boolean more = false;

        final int cl = child.mLeft;
        final int ct = child.mTop;
        final int cr = child.mRight;
        final int cb = child.mBottom;
        ......
        if (hardwareAccelerated) {
            // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
            // retain the flag's value temporarily in the mRecreateDisplayList flag
            child.mRecreateDisplayList = (child.mPrivateFlags & INVALIDATED) == INVALIDATED;
            child.mPrivateFlags &= ~INVALIDATED;
        }

        child.computeScroll();
        .....
   }

ViewGroup绘制的时候,绘制方法dispathDraw中调用了drawChild方法,drawChild方法中又调用了View.java的computeScroll()方法。

由此设想,在view绘制过程中调用computeScroll(),如果我们在computeScroll()中通过scrollTo将view移动到我们想要的位置(即Scroller中的mCurrX,mCurry位置),不停绘制,不停移动,就能达到平滑移动的效果。

怎么控制循环调用computeScroll直到移动到我们想要的位置呢?


@Override
	public void computeScroll() {
		if(mScroller.computeScrollOffset()){//判断平滑移动是否结束,并且计算当前移动到的位置,保存在mCurrX, mCurrY中
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//移动
			postInvalidate();//重绘
		}
	}

关键是computeScroll()实现,如上:computeScroll方法通过computeScrollOffset计算当前时刻应该移动到的位置,scrollTo将布局移动到此位置,并调用重绘,重绘时又会调用computeScroll方法,直至mScroller.computeScrollOffset()方法返回true,即平滑移动结束。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值