參考《android開發藝術探索》 ,參考 http://blog.csdn.net/guolin_blog/article/details/48719871
package com.example.lenvov.com.widget;
import android.content.Context;import android.support.v4.view.ViewConfigurationCompat;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.Scroller;/** * Created by lenvov on 2017/2/7. * 目的:View或者ViewGroup的內容發生移動時比較平滑或者有其他的移動漸變效果 * 原理--利用scroller實現滑動的工作機制 * 1)觸摸事件onTouchEvent獲取坐標,mScroller.startScroll保存參數,invalidate請求重繪View樹,即draw()過程 * 2)View的onDraw會調用computeScroll(空實現) * 3)我們重寫computeScroll,如果滾動未結束則滾動,接著調用invalidate請求再次重繪 * *通俗的理解: * 從上可見Scroller執行流程里面的三個核心方法 * mScroller.startScroll() * mScroller.computeScrollOffset() * view.computeScroll() * 1 在mScroller.startScroll()中為滑動做了一些初始化準備. * 比如:起始坐標,滑動的距離和方向以及持續時間(有默認值)等. * 其實除了這些,在該方法內還做了些其他事情: * 比較重要的一點是設置了動畫開始時間. * * 2 computeScrollOffset()方法主要是根據當前已經消逝的時間 * 來計算當前的坐標點并且保存在mCurrX和mCurrY值中. * 因為在mScroller.startScroll()中設置了動畫時間,那么 * 在computeScrollOffset()方法中依據已經消逝的時間就很容易 * 得到當前時刻應該所處的位置并將其保存在變量mCurrX和mCurrY中. * 除此之外該方法還可判斷動畫是否已經結束. * * 3mScroller.computeScrollOffset()判斷了滑動是否結束 * 2.1 返回false,滑動已經結束. * 2.2 返回true,滑動還沒有結束. * 并且在該方法內部也計算了最新的坐標值mCurrX和mCurrY. * 就是說在當前時刻應該滑動到哪里了. * 既然computeScrollOffset()如此貼心,盛情難卻啊! * 于是我們就覆寫View的computeScroll()方法, * 調用scrollTo(By)滑動到那里!滿足它的一番苦心吧. * */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中的每一個子控件在水平方向上進行布局 layout(int l, int t, int r, int b) 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) { Log.v("srcoll_","MotionEvent.ACTION="+event.getAction()+";getScrollX()="+getScrollX()); 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: //Log.v("srcoll","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()) {// true表示滾動未結束,false表示滾動結束 Log.v("srcoll_","computeScroll getScrollX()="+getScrollX()); scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//獲取當前Scrollx Scrolly,通過scrollto滾動到 invalidate(); }else{ Log.v("srcoll_",""); } }}新聞熱點
疑難解答