View基礎(chǔ)知識
- View本身可以是單個控件,也可以是多個控件組成的一組控件
- ViewGroup也繼承了View
View的位置參數(shù)
top:左上角縱坐標(biāo)
left:左上角橫坐標(biāo)
bottom:右下角縱坐標(biāo)
right:右下角橫坐標(biāo)
x,y:左上角坐標(biāo)
translatioX,translationY:左上角相對于父容器的坐標(biāo)偏移量,默認(rèn)值為0
x = left + translationX;
y = top + translationY;
MotionEvent和TouchSlop
- MotionEvent
在手指接觸到屏幕,觸發(fā)的典型事件類型:
- ACTION_DOWN:手指剛接觸屏幕
- ACTION_MOVE:手指在屏幕上移動
- ACTION_UP:手指離開屏幕
通過MotionEvent對象可得點擊事件發(fā)生的x,y坐標(biāo)
- getX/getY:獲得相對于View左上角x,y的坐標(biāo)
- getRowX/getRowY:獲得相對于手機(jī)屏幕左上角的x,y坐標(biāo)
- TouchSlop
系統(tǒng)所能識別滑動的最小距離,是個常量,也就是滑動的距離小于這個常量系統(tǒng)就認(rèn)為沒有進(jìn)行滑動操作
獲得常量方式:ViewConfiguration.get(getContext()).getScaledTouchSlop()
VelocityTracker、GestureDetector和Scroller
- VelocityTracker:速度追蹤
用于追蹤手指在滑動過程中的速度,包含水平和垂直上的速度
- 首先在View的onTouchEvent方法中追蹤當(dāng)前單擊事件的速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
- 接著,計算速度,獲取速度之前要計算速度,這里的速度指一段時間手指所劃過的像素數(shù)
velocityTracker.computeCurrentVelocity(1000);//計算速度
int xVelocity = (int)velocityTracker.getXVelocity();
int yVelocity = (int)velocityTracker.getYVelocity();
- 最后,調(diào)用clear方法重置并回收內(nèi)存
velocityTracker.clear();
velocityTracker.recycle();
- GestureDetector:手勢檢測
用來輔助檢測用戶的單擊、滑動、長按、雙擊等行為
- Scroller:彈性滑動,實現(xiàn)有過渡效果的滑動
View的滑動
方式:
- 通過View的ScrollTo/ScrollBy方法
- 通過動畫給View施加平移效果來實現(xiàn)滑動
- 通過改變View的LayoutParams使得View重新布局從而實現(xiàn)滑動
使用scrollTo/scrollBy
scrollBy最終也是使用scrollTo來實現(xiàn)的,他們都只能該變View內(nèi)容的位置,不能該變View的布局位置,從左往右滑,mScrollX為正,反之為負(fù),從下往上滑mScrollY為正,反之為負(fù)
使用動畫方式(屬性動畫)
例:將一個View在100ms內(nèi)從原始位置向右平移100像素
ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
View動畫是對View的影像做操作,并沒有該變View的位置參數(shù),要使動畫的狀態(tài)得以保存,要將屬性fillAfter設(shè)置為true,否則動畫完成后結(jié)果就會消失,屬性動畫不會出現(xiàn)此問題
改變布局參數(shù)
即改變LayoutParams,例如通過marginLeft參數(shù)可以達(dá)到向右平移的效果,還可以預(yù)先設(shè)置一個空View等
3種滑動方式的比較
- ScrollTo/ScrollBy:操作簡單,適合對View內(nèi)容的滑動,不影響內(nèi)部元素的單擊事件,缺點:只能滑動View的內(nèi)容,不能滑動View本身
- 動畫:不能該變View本身屬性,適合不與用戶交互的View和實現(xiàn)復(fù)雜動畫
- 該變布局:操作復(fù)雜,適用于與用戶交互的View
彈性滑動
即漸進(jìn)式滑動,主要思想是將一次很大的滑動分成若干個小的滑動,并且在一段時間內(nèi)完成
實現(xiàn)方式:Scroller、Handler.postDelayed、Thread.sleep
Scroller
典型使用方法:
Scroller scroller = new Scroller(mContext);
//緩慢移動到指定位置
private void smoothScrollerTo(int destX, int destY) {
int scrollX = getScrollX();
int destX = destX - scrollX;
//1s內(nèi)滑動destX
mSroller.startScroll(scrollX, 0, deltaX, 0, 1000);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mSroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
注意:這里的滑動是指內(nèi)容上的滑動,View本身是沒有改變的,在上述中,僅僅調(diào)用startScroll方法是無法讓View進(jìn)行滑動的,它的內(nèi)部沒有做滑動相關(guān)的事情,只是保存了些數(shù)據(jù)而已,真正讓View滑動的是invalidate方法,這個方法導(dǎo)致View重繪,在View重繪的draw方法中又會去調(diào)用computeScroll方法,這個方法又會去向Scroller獲取當(dāng)前的scrollX和scrollY,然后通過scrollTo方法實現(xiàn)滑動
computeScrollOffset方法返回的是true表示滑動沒有結(jié)束,返回false,表示滑動結(jié)束
Scroller本身不能實現(xiàn)View滑動,要配合View的computeScroll方法才能完成彈性滑動的效果
View事件分發(fā)機(jī)制
點擊事件傳遞規(guī)則
點擊事件分析的對象是MotionEvent,點擊事件分發(fā)過程是由:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent方法共同完成的
- dispatchTouchEvent:用于事件的分發(fā),如果事件傳遞到當(dāng)前View,那么此方法一定會被調(diào)用,返回結(jié)果受當(dāng)前View的onTouchEvent和下級View的dispatchTouchEvent影響
- onInterceptTouchEvent:在上述方法內(nèi)部調(diào)用,判斷是否攔截此事件,true為攔截
- onTouchEvent:在dispatchTouchEvent方法中調(diào)用,用來處理點擊事件
對于ViewGroup來說,點擊事件后,調(diào)用dispatchTouchEvent方法,此時ViewGroup的onInterceptTouchEvent方法被調(diào)用,返回true代表攔截,接著調(diào)用onTouchEvent處理事件,如果返回false,分發(fā)給子元素
當(dāng)View要處理事件時,如果設(shè)置了onTouchListener,此時onTouch方法會被回調(diào),返回false,view的ouTouchEvent會被調(diào)用,返回true,不會調(diào)用。onTouchListener優(yōu)先級比onTouchEvent優(yōu)先級高,onClickListener優(yōu)先級最低
事件點擊后傳遞順序:Acticity,window,view,如果view不處理事件就會向上傳遞處理
總結(jié):
- 同一個事件序列指手指觸摸屏幕到離開這一過程事件
- 正常情況,一個事件序列只能給一個View處理
- 一旦一個View攔截了一個事件,那么它的同一個事件序列都直接交給它處理,并且它的onInterceptTouchEvent方法不再調(diào)用
- 某個View一旦開始處理事件,如果不消耗ACTION_DOWN事件的話,那么其它同一系列事件它也不會消耗,并且重新交給父元素處理
- 如果View不消耗ACTION_DOWN以外的其他事件,那么這個點擊事件就會消失,并且父元素的onTouchEvent方法也不會調(diào)用,View可以持續(xù)收到后續(xù)的事件,最終這些事件都會交由Activity來處理
- ViewGroup默認(rèn)不攔截任何事件
- View沒有onInterceptTouchEvent方法,一旦有事件傳遞給它,他就調(diào)用onTouchEvent方法
- View的onTouchEvent方法都默認(rèn)會消耗事件的,除非它是不可點擊的
- View的enable屬性不影響onTouchEvent的返回值
- 事件的傳遞方向是從外向內(nèi)的,總是由父元素傳遞給子元素,子元素再傳遞給View
- onClick發(fā)生的前提是View可點擊并且收到了down和up事件