《Android開發藝術探索》——View事件體系
自定義控件、滑動沖突解決
View基礎知識
- View的位置參數
- MotionEvent和TouchSlop對象
- VelocityTracker
- GestureDetector和Scroller對象
1. View的位置參數
2. MotionEvent和TouchSlop
注意:各個方法相對目標不一樣。
view獲取自身坐標:getLeft(),getTop(),getRight(),getBottom()
view獲取自身寬高:getHeight(),getWidth()
motionEvent獲取坐標:getX(),getY(),getRawX(),getRawY()
- MotionEvent
Touch事件中,典型的事件有如下幾種:- ACTION_DOWN —— 手指接觸屏幕
- ACTION_MOVE —— 手指在屏幕上移動
- ACTION_UP —— 手指從屏幕離開
- TouchSlop
TouchSlop是系統所能識別出的被認為是滑動的最小距離,換句話說,當手指在屏幕上滑動時,當兩次滑動之間的距離小于這個常量,那么系統不認為是在進行滑動操作。通過以下方式獲取這個常量值:ViewConfiguration.get(getContext()).getScaledTouchSlop();
可以使用這個常量判斷是否達到滑動條件,處理滑動時,做一些過濾。
3. VelocityTracker、GestureDetector和Scroller對象
- VelocityTracker
速度追蹤,用于追蹤手指在滑動過程中的速度,包括水平和豎直方向的速度。
構造方法中初始化獲取VelocityTracker對象VelocityTracker mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000); float xVelocity = mVelocityTracker.getXVelocity(); float yVelocity = mVelocityTracker.getYVelocity();
mVelocityTracker.clear(); mVelocityTracker.recycle();
-
GestureDetecor
手勢檢測,用于輔助檢測用戶的單擊、滑動、長按、雙擊等行為。
創建一個 GestureDetecor 對象并實現 OnGestureListener 接口,根據需要實現單擊等方法:
GestureDetector mGestureDetector = new GestureDetector(this); // 解決長按屏幕后無法拖動的現象 mGestureDetector.setIsLongpressEnabled(false);
接管目標 View 的 onTouchEvent 方法,在待監聽 View 的 onTouchEvent 方法中添加如下實現:
boolean consume = mGestureDetector.onTouchEvent(event); return consume;
建議:
如果只是監聽滑動操作,建議在onTouchEvent中實現;如果要監聽雙擊這種行為,則使用GestureDetector 。
- Scroller
彈性滑動對象,用于實現View的彈性滑動。
View的scrollTo/scrollBy方法來滑動時,過程是瞬間完成的。使用Scroller則有過渡滑動的效果。注意,Scoller本身無法讓View彈性滑動,它需要和View的computerScroller方法配合使用。
構造方法初始化Scroller mScroller = new Scroller(getContext());
private void smoothScrollBy(int dx, int dy) { mScroller.startScroll(getScrollX(), 0, dx, 0, 500); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
原理圖
當在MotionEvent.ACTION_UP事件觸發時,調用startScroll方法,并調用invalidate/postInvalidate方法,會導致View重繪,執行View.draw方法。在此方法中會調用View.computeScroll方法,此方法是空實現,需要我們自己處理邏輯。具體邏輯是:先判斷computeScrollOffset,如果為true,表示滾動未結束。則執行scrollTo方法,再次調用postInvalidate,如此反復執行,直到結束。
computeScrollOffset方法計算了一小段時間間隔內偏移的距離,即CurrX,CurrY。并返回是否滾動結束的標記。true表示未結束,false表示結束。
View的scrollTo/scrollBy方法操作的View的內容滑動。
getScrollX返回的是View的左邊緣到其內容左邊緣的距離。相對于View的左邊緣
getScrollY返回的是View的上邊緣到其內容上邊緣的距離。
如果View的內容向左滑,滑出View的左邊界,getScrollX為正值,反之為負值。
如果View的內容向上滑,滑出View的上邊界,getScrollY為正值,反之為負值。
getScrollX和getScrollY的變化示意圖
來自:http://www.jianshu.com/p/efcf275a7e00