Android,自定义ScrollView、Scroller的基本使用

ScrollView的常用方法

(大多重写自View,HorizontalScrollView也一样)

public final int getScrollX()
返回值表示当前滚动到内容的哪个位置

Android,自定义ScrollView、Scroller的基本使用_第1张图片

public void scrollTo(int x, int y)
滚动到内容的x或y位置,一步到位,没有滚动动画,滚动动画需要利用Scroller定义

public void scrollBy(int x, int y)
往内容的正或负方向滚动x或y距离

fling过程中屏蔽了scrollTo()和scrollBy()
如果scrollTo()或scrollBy()超出内容的边界,则止于边界(默认)。

public final void smoothScrollTo(int x, int y)
平滑滚动到内容的x或y位置
当直接调用smoothScrollTo()没有效果时,试试这种方法:
View#post(new Runnable() {
    @Override
    public void run() {
        smoothScrollTo(x, y);
    }
});

Scroller的常用方法

public void startScroll(int startX, int startY, int dx, int dy, int duration)
开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达偏移坐标为(startX+dx , startY+dy)处

public boolean computeScrollOffset()
滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX和mCurrY值中

public final int getCurrX()
返回mCurrX

自定义ScrollView

import android.content.Context;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
import android.widget.OverScroller;

public class MyScrollView extends HorizontalScrollView {
    private static final String TAG = "MyScrollView";
    private OverScroller mScroller; //OverScroller >= Scroller

    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new OverScroller(context);
    }

    /**
     * 只要内容正在滚动(手指滑动+fling),就会连续触发onScrollChanged()
     * onScrollChanged()的触发频率与onTouchEvent()一致
     * 当scroll速度慢或者scroll停止时,同一个位置会执行两次
     * @param scrollX = getScrollX(),可取最值,各取两次
     * @param scrollY
     * @param oldScrollX = 上一次执行onScrollChanged()时的scrollX值
     * @param oldScrollY
     */
    @Override
    protected void onScrollChanged(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        Log.d(TAG, "onScrollChanged: scrollX = " + scrollX + ",oldScrollX=" + oldScrollX);
        super.onScrollChanged(scrollX, scrollY, oldScrollX, oldScrollY);
    }

    /**
     * 只要内容正在滚动(手指滑动+fling),就会连续触发onOverScrolled()
     * onOverScrolled()的触发频率与onTouchEvent()一致,同一个位置不会执行两次
     * @param scrollX 当前位置,可取最值,各取一次,getScrollX()=参数scrollX上一次的取值
     * @param scrollY
     * @param clampedX 当scroll到左/右边界时,值为true
     * @param clampedY 当scroll到上/下边界时,值为true
     */
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        Log.d(TAG, "onOverScrolled: scrollX=" + scrollX + ",scrollY=" + scrollY);
        Log.d(TAG, "onOverScrolled: clampedX=" + clampedX + ",clampedY=" + clampedY);
        //去掉这一行,会导致异常,滑不动
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
    }

    /**
     * 只要内容正在滚动(手指滑动+fling),就会连续触发overScrollBy()
     * 但scroll停止的那一刻不执行此方法,即delta*不取0值,scroll*不取最值
     * overScrollBy()的触发频率与onTouchEvent()一致
     */
    @Override
    protected boolean overScrollBy(int deltaX,
                                   int deltaY,//与内容滚动速度成正相关,scroll将停未停时,deltaY为1或-1,不取0值
                                   int scrollX,
                                   int scrollY,//=getScrollY(),不取最值
                                   int scrollRangeX,
                                   int scrollRangeY,//控件内容可scroll的总跨度
                                   int maxOverScrollX,
                                   int maxOverScrollY,//最大可OverScroll的值,默认为0
                                   boolean isTouchEvent//手指滑动带动scroll时,true;手指抬起,惯性fling时,false。
    ) {
        Log.d(TAG, "overScrollBy deltaX = " + deltaX);
        Log.d(TAG, "overScrollBy deltaY = " + deltaY);
        Log.d(TAG, "overScrollBy scrollX = " + scrollX);
        Log.d(TAG, "overScrollBy scrollY = " + getScrollY());
        Log.d(TAG, "overScrollBy scrollRangeX = " + scrollRangeX);
        Log.d(TAG, "overScrollBy scrollRangeY = " + scrollRangeY);
        Log.d(TAG, "overScrollBy maxOverScrollX = " + maxOverScrollX);
        Log.d(TAG, "overScrollBy maxOverScrollY = " + maxOverScrollY);
        Log.d(TAG, "overScrollBy isTouchEvent = " + isTouchEvent);
        //直接return true或者false,都会导致异常,滑不动
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    }

    /**
     * fling过程中不执行onTouchEvent()
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent: getScrollX=" + getScrollX());
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onTouchEvent: up");

                // 分页效果demo----------------------------------------------------
                mScroller.startScroll(getScrollX(), 0, 2000 - getScrollX(), 0, 2000);
                //执行ScrollView的invalidate(),触发computeScroll()
                invalidate();
                // ---------------------------------------------------------------

                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 一次scroll手势中,当手指离开屏幕时执行一次fling()
     * @param velocityX:手指离开时scroll的速度(fling的初速度);内容向正方向滚,速度值为正值;内容向负方向滚,速度值为负值
     */
    @Override
    public void fling(int velocityX) {
        Log.d(TAG, "fling: velocityX = " + velocityX);
        super.fling(velocityX);
    }

    /**
     * 只要内容正在滚动(手指滑动+fling),就会连续触发computeScroll()
     */
    @Override
    public void computeScroll() {
        /**
         * isMainThread为true表示该方法在主线程中执行
         */
        boolean isMainThread = Looper.myLooper() == Looper.getMainLooper();
        Log.d(TAG, "computeScroll in main thread : " + isMainThread);

        // 分页效果demo------------------------------------------------------------
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //执行ScrollView的invalidate(),触发computeScroll()
            invalidate();
        }
        // -----------------------------------------------------------------------
    }
}

回调顺序

或者Android,自定义ScrollView、Scroller的基本使用_第2张图片

onScrollChanged执行两次log

 Android,自定义ScrollView、Scroller的基本使用_第3张图片

Demo:实现分页效果

https://github.com/chulichao/PageScrollView/blob/master/app/src/main/java/com/clc/psv/PageScrollView.java

你可能感兴趣的:(Android)