學習使用安卓scroller
阿新 • • 發佈:2018-11-05
Android Scroller 解讀編寫Demo
Scroller是一個專門用於處理滾動效果的工具類,大多數情況下,我們直接使用Scroller的場景並不多,但是很多大家所熟知的控制元件在內部都是使用Scroller來實現的,如ViewPager、ListView等。而如果能夠把Scroller的用法熟練掌握的話,對於之後使用ViewPager更容易上手。
直接上原始碼,程式碼已經做了詳細解讀:
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 attr) { super(context,attr); // 第一步,建立Scroller的例項 mScroller=new Scroller(context); ViewConfiguration configuration=ViewConfiguration.get(context); //能夠識別的最小滑動舉例 ViewConfiguration.getScaledTouchSlop(); //獲取TouchSlop值 mTouchSlop= configuration.getScaledTouchSlop(); } //定義View的位置在ViewGroup中的位置 //1)引數changed表示view有新的尺寸或位置; //2)引數l表示相對於父view的Left位置; //3)引數t表示相對於父view的Top位置; //4)引數r表示相對於父view的Right位置; //5)引數b表示相對於父view的Bottom位置。. @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(a,b,c,d)中的四個引數是相對於父佈局定義的 //a和 b 是控制元件左邊緣和上邊緣相對於父類控制元件左邊緣和上邊緣的距離。 // c 和 d是空間右邊緣和下邊緣相對於父類控制元件左邊緣和上邊緣的距離。 childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight()); } // 初始化左右邊界值 leftBorder = getChildAt(0).getLeft(); rightBorder = getChildAt(getChildCount() - 1).getRight(); } } //View本身大小多少,由onMeasure()來決定 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //返回包含View的個數 int childCount=getChildCount(); for(int i=0;i<childCount;i++){ //獲取佈局中的view View childView=getChildAt(i); //規定了每個子view的尺寸,與當前的父View保持一致 measureChild(childView,widthMeasureSpec,widthMeasureSpec); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ //手指按下 case MotionEvent.ACTION_DOWN: //獲取絕對座標,相對於螢幕的X座標 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); //getScrollX() 就是當前view的左上角相對於母檢視的左上角的X軸偏移量 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; //手指離開螢幕 case MotionEvent.ACTION_UP: // 當手指擡起時,根據當前的滾動值來判定應該滾動到哪個子控制元件的介面 int targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); int dx = targetIndex * getWidth() - getScrollX(); // 第二步,呼叫startScroll()方法來初始化滾動資料並重新整理介面 //invalidate()函式的主要作用是請求View樹進行重繪 // startScroll執行過程中即在duration時間內, // computeScrollOffset 方法會一直返回false,但當動畫執行完成後會返回返加true. 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()函式的主要作用是請求View樹進行重繪 invalidate(); } }
activity_main.xml
參考:http://blog.csdn.net/guolin_blog/article/details/48719871
http://www.jianshu.com/p/0c863bbde8eb
http://blog.csdn.net/pi9nc/article/details/18764863