Android Scroller及實際使用

什麼是Scroller

我們知道View中有兩個方法可以實現滾動/位置變化,scrollTo/scrollBy

/**
     * Set the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the x position to scroll to
     * @param y the y position to scroll to
     */
    public void scrollTo(int x, int y) `
/**
     * Move the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the amount of pixels to scroll by horizontally
     * @param y the amount of pixels to scroll by vertically
     */
    public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

那爲什麼view已經有了滾動的能力還要一個Scroller去單獨實現一套:

  1. 自帶的效果比較生硬(瞬間完成),沒有中間的過度效果,所以需要scroller

Sroller 使用

郭霖老師文章裏的使用:

public class ScrollerLayout extends ViewGroup {

    private static final String TAG = "ScrollerLayout";

    /**
     * 用於完成滾動操作的實例
     */
    private Scroller mScroller;

    /**
     * 判定爲拖動的最小移動像素數
     */
    private int mTouchSlop;

    /**
     * 手機按下時的屏幕座標
     */
    private float mXDown;

    /**
     * 手機當時所處的屏幕座標
     */
    private float mXMove;

    /**
     * 上次觸發ACTION_MOVE事件時的屏幕座標
     */
    private float mXLastMove;

    /**
     * 界面可滾動的左邊界
     */
    private int leftBorder;

    /**
     * 界面可滾動的右邊界
     */
    private int rightBorder;

    public ScrollerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 第一步,創建Scroller的實例
        mScroller = new Scroller(context);
        ViewConfiguration configuration = ViewConfiguration.get(context);
        // 獲取TouchSlop值
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
        Log.d(TAG, "ScrollerLayout: default=" + ViewConfigurationCompat.getScaledPagingTouchSlop(configuration)
                + " mTouchSlop=" + mTouchSlop);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.d(TAG, "onMeasure: widthMeasureSpec=" + widthMeasureSpec + " heightMeasureSpec=" + heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 爲ScrollerLayout中的每一個子控件測量大小
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                // 爲ScrollerLayout中的每一個子控件在水平方向上進行佈局
                childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
            }
            // 初始化左右邊界值
            leftBorder = getChildAt(0).getLeft();
            rightBorder = getChildAt(getChildCount() - 1).getRight();

            Log.d(TAG, "onLayout: changed=" + changed + "   l=" + l + " t=" + t + " r=" + r + " b=" + b
                    + " leftBorder=" + leftBorder + "  rightBorder=" + rightBorder);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mXDown = ev.getRawX();
                mXLastMove = mXDown;
                break;
            case MotionEvent.ACTION_MOVE:
                mXMove = ev.getRawX();
                float diff = Math.abs(mXMove - mXDown);
                mXLastMove = mXMove;
                // 當手指拖動值大於TouchSlop值時,認爲應該進行滾動,攔截子控件的事件
                if (diff > mTouchSlop) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "onTouchEvent: event=" + event.getAction());
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mXMove = event.getRawX();
                int scrolledX = (int) (mXLastMove - mXMove);
                if (getScrollX() + scrolledX < leftBorder) {
                    scrollTo(leftBorder, 0);
                    return true;
                } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
                    scrollTo(rightBorder - getWidth(), 0);
                    return true;
                }
                scrollBy(scrolledX, 0);
                mXLastMove = mXMove;
                break;
            case MotionEvent.ACTION_UP:
                // 當手指擡起時,根據當前的滾動值來判定應該滾動到哪個子控件的界面
                int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
                int dx = targetIndex * getWidth() - getScrollX();
                // 第二步,調用startScroll()方法來初始化滾動數據並刷新界面
                mScroller.startScroll(getScrollX(), 0, dx, 0);
                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        Log.d(TAG, "computeScroll: ");
        // 第三步,重寫computeScroll()方法,並在其內部完成平滑滾動的邏輯
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
}

佈局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.guolin.scrollertest.MainActivity">
    <Button
        android:id="@+id/scroll_to_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="scrollTo"/>
    <Button
        android:id="@+id/scroll_by_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="scrollBy"/>
</LinearLayout>
  1. TouchSlop是做什麼用的
    view跟隨手指移動的最小距離,小於這個距離,view不會跟隨手指移動

Scroller 原理

Scroller 原理

Scroller 使用場景

參考

http://blog.csdn.net/guolin_blog/article/details/48719871

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章