3.1 View的基礎知識android
主要有:View的位置參數,MotionEvent和TouchSlop對象,VelocityTracker,GestureDetector和Scroller對象ide
3.1.1 View佈局
view是android中全部控件的基類,是一種界面層的控件的一種抽象.post
ViewGroup(控件組),內部包含了不少個控件,即一組view動畫
3.1.2 View的位置參數this
View的位置主要由它的四個頂點來決定:top(左上角縱座標),left(左上角橫座標),right(右下角橫座標),bottom(右下角縱座標)spa
left = getLeft();code
right = getRight();對象
top = getTop();blog
bottom = getBottom();
從android3.0開始,增長了:x,y,translationX和translationY,x和y是View的左上角的左邊,而translationX和translationY是View左上角相對於父容器的偏移量.
x = left + translationX;
y = right + translationY;
3.1.3 MotionEvent和TouchSlop
1. MotionEvent
手指接觸屏幕後產生一些列的事件,典型的事件以下
2. TouchSlop
是系統所能識別出被認爲是滑動的最小距離.若是兩次滑動之間的距離小過這個常量,系統就不認爲你是在進行滑動操做
經過ViewConfiguration.get(getContext()).getScaledTouchSlop()得到這個常量.
3.1.4 VelocityTracker,GestureDetector和Scroller
1.VelocityTracker
速度追蹤:用來追蹤手指在滑動過程當中的速度
首先在View的onTouchEvent方法中追蹤當前點擊事件的速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
注意兩點:第一,獲取速度以前必須先計算速度,即在getXVelocity()和getYVelocity()方法以前要先調用computeCurrentVelocity方法,第二點,這裏的速度是指一段時間內手指所劃過的像素數
velocityTracker.computeCurrentVelocity(1000);//時間間隔爲1000ms;
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();
2. GestureDetector
手勢檢測
首先須要建立一個GestureDetector對象並實現OnGestureListener接口
GestureDetector mGestureDetector = new GestureDetector(this);
//解決長按屏幕後沒法拖動的現象
mGestureDetector.setIsLongpressEnabled(false);
接着,接管目標View的onTouchEvent方法,在待監聽View的onTouchEvent方法中實現
boolean consume = mGestureDetector.onTouchEvent(event);
retrun consume;
3. Scroller
彈性滑動對象,用於實現View的彈性滑動,使用scrollTo/scrollBy方法來進行滑動時,其過程是瞬間完成的.
3.2 View的滑動
3.2.1 使用scrollTo/scrollBy
scrollBy實際上也是調用了scrollTo方法,scrollBy它實現基於當前位置的相對滑動,scrollTo是基於所傳遞參數的絕對滑動
View內部的兩個屬性mScrollX(等於View左邊緣和View內容左邊緣在水平方向的距離)和mScrollY(等於View的上邊緣和View內容上邊緣在豎直方向的距離)的改變規則,經過getScrollX和getScrollY得到
從左往右滑動,mScrollX爲負值,反之爲正值;若是從上往下滑動,那麼mScrollY爲負值,反之爲正值.
3.2.2 使用動畫
使用View動畫,可是會形成View的位置沒有改變,動畫完成後,View仍是會停在原來的位置,因此要用屬性動畫
3.2.3 改變佈局參數
佈局參數:LayoutParams
MarginLayoutParams params = (MarginLayoutParams) mButton1.getLayoutParams(); params.width += 100; params.leftHeight += 100; mButton1.requestLayout(); //或者mButton1.setLayoutParams(params);
3.3 彈性滑動
3.3.1 使用scroller
Scroller Scroller = new Scroller(mContext); //緩慢滾動到指定位置 private void smoothScrollTo(int destX, int destY){ int scrollX = getScrollX(); int deltaX = destX - scrollX; //1000ms內滑向的destX,效果就是慢慢滑動 mScroller.startScroller(scrollX, 0, deltaX, 0, 1000); invalidate(); } @Override public void computeScroller(){ if (mScroller.computeScrollOfset()) { mscrollTo(mScroller.getScrollX, mScroller.getCurrY()); postInvalidate(); } }
Scroller自己並不能實現view的滑動,他須要配合View的computeScroll方法才能完成彈性滑動的效果它不斷讓view重繪,每一次重繪距滑動起始時間會有一個時間間隔,經過這個時間間隔就能夠得出View當前的滑動位置,知道了滑動位置就能夠經過scrollTo方法完成View的滑動.
3.3.2 經過動畫
動畫ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(1000).start();
3.3.3 使用延時策略
使用Handler發送消息sendMessage(),在handleMessage()方法中進行view的繪製
3.4 View的事件分發機制
3.4.1 點擊事件的傳遞規則
有三個方法:
public boolean dispatchTouchEvent(MotionEvent ev):用來進行事件的分發.若是事件可以傳遞給當前View,那麼次方法必定會被調用,返回結果受當前View的onTouchEvent和下級View的dispatchTouchEvent方法的影響,表示是否消耗當前事件.
public boolean onInterceptTouchEvent(MotionEvent event):用來判斷是否攔截某個事件,若是當前view攔截了某個事件,那麼在同一個事件序列當中,此方法不會再次調用,返回結果表示是否攔截當前事件
public boolean onTouchEvent(MotionEvent event):在dispatchTouchEvent方法中調用,用來處理點擊事件,返回結果表示是否消耗當前事件,若是不消耗,則在同一事件序列中,當前View沒法再次接收到事件.
一個點擊事件產生後,它的傳遞過程遵循以下順序:Activity->Window->View.
3.4.2 事件分發
1.Activity對點擊事件的分發過程
點擊事件用MotionEvent來表示,事件最早傳遞給當前Activity,由Activity的dispatchTouchEvent來進行事件派發,具體的工做是有Activity內部的Window來完成的.
Window會將事件傳遞給decor view ,decor view 通常就是當前界面的底層容器,經過Activity.getWindow.getDecorView()能夠得到.
Window是一個抽象類,實現類是PhoneWindow,PhoneWindow將時間直接傳遞給DecorView
經過((ViewGroup)getWindow().getDecorView().findViewById(android.R.id.content)).getChildAt(0)這種方式獲取Activity所設置的View
而咱們經過setContentView設置的View是它的一個子View.
2. 頂級View對點擊事件的分發過程
點擊事件到達頂級View(通常是一個ViewGroup)之後,會調用ViewGroup的dispatchTouchEvent方法
若是頂級ViewGroup攔截事件,即onInterceptTouchEvent返回true,則事件由ViewGroup處理,這時若是ViewGroup的mOnTouchListener被設置,則onTouchEvent會被調用,不然onTouchEvent會被調用.
onTouch會屏蔽掉onTouchEvent,
在onTouchEvent中,若是設置了mOnClickListener,則onClick會被調用.
若是頂級View不攔截,則事件會傳遞給它所在的點擊事件鏈上的子View.
3. View對點擊事件的處理過程
首先會判斷有沒有設置OnTouchListener,若是OnTouchListener中的onTouch方法返回true,那麼onTouchEvent就不會被調用,OnTouchListener的優先級高於onTouchEvent
只要View的CLICKABLE和LONG_CLICKABLE有一個爲true,那麼他就會消耗這個事件,即onTouchEvent方法會返回true
3.5 View的滑動衝突
1. 外部滑動方向跟內部滑動方向不一致
2.外部滑動方向和內部滑動方向一致
3.上面兩種狀況的嵌套
3.5.2 滑動衝突的處理規則
根據滑動時水平滑動仍是豎直滑動來判斷到底由誰來攔截事件
根據具體的業務尋找具體規則
3.5.3 滑動衝突的解決方法
1.外部攔截法
點擊事件都先通過父容器的攔截處理,若是父容器須要此事件被攔截,若是不須要此事件就不攔截
外部攔截髮須要重寫父容器的onInterceptTouchEvent方法,在內部作相應的攔截便可
須要攔截,intercepted返回true,不攔截返回false
2.內部攔截法
指父容器不攔截任何事件,全部的事件都傳遞給子元素,若是子元素須要此事件就直接消耗掉,不然就交由父容器進行處理,須要配合requestDisallowInterptTouchEvent方法
須要重寫子元素的dispatchTouchEvent方法.