1. 程式人生 > >Android 自定義View之Scroller處理滾動工具類詳解

Android 自定義View之Scroller處理滾動工具類詳解

public class ScrollerLayout extends ViewGroup { /** * 用於完成滾動操作的例項 */ 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); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, 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(); } } @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) { 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() { // 第三步,重寫computeScroll()方法,並在其內部完成平滑滾動的邏輯 if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } } }