OverScroller的一些重要方法和属性

OverScroller与Scroller类类似,都是实现弹性滑动、惯性滑动的辅助类,只不过OverScroller出现的比较晚,功能更全面一些,可以实现边界回弹等。
之所以说它是一个辅助类,是因为它本身并不能移动View,真正移动View的实现需要我们在回调方法computeScroll()中自己实现。

基础方法

getStartX()和getStartY()

getStartX() 滑动的起点x坐标 ,对应startX的值
getStartY() 滑动的起点y坐标 ,对应startY的值

getFinalX()和getFinalY()

getFinalX() 滑动的终点x坐标,通过 startX + dx 计算得来,对应源码mFinal = start + distance
getFinalY() 滑动的终点y坐标,通过 startY + dy 计算得来

getCurrX()和getCurrY()

getCurrX() 滑动时,该方法返回当前x轴坐标,随着时间的推移最终等于终点坐标
getCurrY() 当前在y轴坐标,随着时间的推移最终等于终点坐标

getCurrVelocity()

getCurrVelocity() 当前滑动的速度,根据X和Y轴速度计算得来

computeScrollOffset()

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

以上get系列方法都有对应的set方法,如setFinalX(int newX) (设置最终停留的水平坐标,没有动画效果,直接跳到目标位置) 等。不过这些set方法一般都用不到或者要避免去使用。如果要实现移动,使用下面的核心方法。
需要再强调一遍,无论是set系列方法还是下面的核心方法,本身并不能移动控件,而是要通过重写computeScroll()方法自己去实现。

核心方法

startScroll

 /**
     * 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.
     * 通过提供一个起点和要移动的距离开始滚动,默认耗时250ms
	 * @param startX x轴起点坐标 ,这里包括下面的不一定是绝对坐标点,事实
     * 上大多情况下都不是。要根据具体实现来区分,如实现滑动使用的是setTranslationY
     *  方法,那么startX可能就是getTranslationY    
     * @param startY y轴起点坐标
     *     
     * @param dx x轴滑动距离 正数内容左移
     *        
     * @param dy Y轴滑动距离 正数内容上移
     *      
     */
    public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }

根据这个方法的参数,可以计算终点坐标,如Y轴终点坐标为startY + dy
使用示例:

    private float mLastY;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mLastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                if (Math.abs(dy) > 5){
                    mScroller.startScroll(0, mScroller.getFinalY(), 0, (int) dy);
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            setTranslationY(mScroller.getCurrY());
            postInvalidate();
        }
    }

fling

    /**
     * 基于一个投掷手势开始滚动。所走的距离将取决于抛掷的初速度。
     *
     * @param startX X轴滚动起点坐标,这里包括下面的不一定是绝对坐标点,事实
     *  上大多情况下都不是。要根据具体实现来区分,如实现滑动使用的是scrollTo方
     *  法,那么startX可能就是getScrollX
     * 
     * @param startY Y轴滚动起点坐标
     * 
     * @param velocityX X轴初速度
     *            
     * @param velocityY Y轴初速度
     *         
     * @param minX 首先要明确的是这个值不是绝对坐标,而是一个距离或者说是一个范围,等于
     * 			   startX减去view向左滚动最终停留的位置坐标FinalX,即StartX - FinalX。
     * 			   如果速度足够大且overX > 0,view在滚动的时候,滚动范围会超出这个值,
     * 			   但随后会自动回弹到minX处,总的来说这个值控制着左边界
     * 
     * @param maxX 和minX类似,只不过,这个值控制的是右边界
     * 
     * @param minY 和minX类似,控制上边界
     * 
     * @param maxY 和minX类似,控制下边界
     * 
     * @param overX 滚动时允许滑出左右边界的范围,如果是0,滚动到边界会立即停止,如
     * 			  果大于0,会有一个回弹效果
     * 
     * @param overY 滚动时允许滑出上下边界的范围
     */
    public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY, int overX, int overY) {
        // Continue a scroll or fling in progress
        if (mFlywheel && !isFinished()) {
            float oldVelocityX = mScrollerX.mCurrVelocity;
            float oldVelocityY = mScrollerY.mCurrVelocity;
            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
                velocityX += oldVelocityX;
                velocityY += oldVelocityY;
            }
        }

        mMode = FLING_MODE;
        mScrollerX.fling(startX, velocityX, minX, maxX, overX);
        mScrollerY.fling(startY, velocityY, minY, maxY, overY);
    }

springBack

/**
 * Call this when you want to 'spring back' into a valid coordinate range.
 * 当你在滑动view松手后,想实现一个“回弹”的效果时,调用这个方法
 * @param startX Starting X coordinate
 * @param startY Starting Y coordinate
 * @param minX 这里同样是一个范围,需要计算,详见下面的例子
 * @param maxX Maximum valid X value
 * @param minY Minimum valid Y value
 * @param maxY Minimum valid Y value
 * @return true if a springback was initiated, false if startX and startY were
 *          already within the valid range.
 */
public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
    mMode = FLING_MODE;

    // Make sure both methods are called.
    final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
    final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
    return spingbackX || spingbackY;
}

OverScroller的一些重要方法和属性_第1张图片

    private int top;//上边界坐标
    private int bottom;//下边界坐标
    private float mLastX;
    private float mLastY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float y = event.getY();
        float x = event.getX();
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = x - mLastX;
                float dy = y - mLastY;
                mScroller.startScroll(0, mScroller.getFinalY(), 0, (int) dy);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int initialVelocity = (int) mVelocityTracker.getYVelocity();
                if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                    fling(initialVelocity);
                } else if (mScroller.springBack(0, (int) getTranslationY(), 0, 0, top - getTop(),
                        bottom - getBottom())) {//minY = top - getTop() 即上边界坐标 - 控件上坐标
                    postInvalidateOnAnimation();
                }
                recycleVelocityTracker();
                break;
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(event);
    }

    public void fling(int velocityY) {
        int minY = top - getTop();//实际上滑允许滚动的范围,等于minY加上overY
        int maxY = bottom - getBottom();//实际下滑允许滚动的范围,等于maxY加上overY
        mScroller.fling(0, (int) getTranslationY(), 0, velocityY, 0, 0, minY, maxY, 0, 50);
        postInvalidateOnAnimation();
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            setTranslationY(mScroller.getCurrY());
            postInvalidate();
        }
    }

demo

通过移动自定义TextView,测试验证OverScroller的相关方法
通过自定义流式布局,滑动内容验证OverScroller的相关方法

你可能感兴趣的:(Android)