View參數概念
1.位置參數
由頂點決定袁滥,對應于View的四個屬性: top(左上角縱坐標)贵白、left(左上角橫坐標)、bottom(右下角縱坐標)睬塌、right(右下角橫坐標)齿税,相對于View的父容器屋剑,view的寬高width = right – left迄薄,height= bottom – top
獲裙潦荨:getLeft(),getRight(), getTop(), getBottom();
從Android3.0開始新增x, y, translationX, translationY
x,y表示view相對于父容器的左上角坐標,translationX, translationY表示view相對于父容器的偏移量至朗,默認值為0
top和left表示原始的位置信息屉符,在平移過程中,translationX爽丹、translationY發(fā)生改變筑煮,進而x, y改變辛蚊。
2.MotionEvent(位移事件)
ACTION_DOWN:手指剛接觸屏幕
ACTION_MOVE:手指在屏幕上移動
ACTION_UP:手指離開屏幕
getX(),?getY()點擊事件相對于當前View的左上角的x和y坐標getRawX(), getRawY()點擊事件相對于屏幕左上角的x和y坐標
3.TouchSlop系統(tǒng)能識別的最小滑動距離
通過ViewConfiguration.get(getContext()).getScaledTouchSlop獲取
4.VelocityTracker追蹤手指滑動過程中的速度
在onTouchEvent中追蹤點擊事件的速度
VelocityTrackervelocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000),?1000ms內劃過的像素
velocityTracker.getXVelocity();水平速度
velocityTracker.getXVelocity();豎直速度
velocityTracker.clear();
velocityTracker.recycle();
5.GestureDetector手勢檢測檢測單擊粤蝎、雙擊、長按袋马、滑動
onSingleTapUp(單擊)初澎、onFailing(快速滑動)、onScroll(拖動)、onLongPress(長按)碑宴、onDoubleTap(雙擊)
6.Scroller彈性滑動對象有過渡效果的滑動
View的滑動
1.scrollTo& scrollBy
關鍵屬性mScrollX,mScrollY分別通過getScrollX()和getScrollY()得到
mScrollX指View左邊緣與View內容左邊緣在水平方向的距離
mScrollY指View上邊緣與View內容上邊緣在豎直方向的距離
當View左移時软啼,mScrollX為正,反之為負
當View上移時延柠,mScrollY為正祸挪,反之為負
scrollTo和scrollBy只能改變View內容的位置,不能改變View在布局中的位置贞间。
2.使用動畫View動畫和屬性動畫
View動畫向右下角平移100
View動畫是對View的影像做操作贿条,并沒有真正改變View的位置參數,動畫完成后結果會消失增热,設置fillAfter屬性為true會保留動畫后的狀態(tài)整以,移動后的View無法觸發(fā)onClick事件
屬性動畫View從原位置向右平移100像素
屬性動畫后動畫結果不會消失,并且在平移動畫之后峻仇,能夠觸發(fā)onClick事件
3.改變布局參數
改變LayoutParams公黑,通過設置margin參數即可達到移動的效果。
scrollTo/scrollBy:適合對View內容的滑動
動畫:適合沒有交互的View和復雜的動畫效果
改變布局參數:適合有交互的View
彈性滑動
1.探究Scoller
當構造一個Scroller對象并調用它的startScroll方法時摄咆,Scroller內部其實什么也沒做凡蚜,只是傳遞了幾個參數并保存了,而invalidate方法會導致View重繪豆同,draw方法又會調用computeScroll方法番刊,而在computeScroll中通過scrollTo方法讓View滑動,接著又調用postInvalidate方法再次重繪影锈,直到滑動過程結束芹务。
而在computeScroll方法中,通過computeScrollOffset判斷當前是否重繪結束鸭廷,通過時間的流逝來計算當前View的位置坐標枣抱。如果經過的時間小于總時間,繼續(xù)重繪辆床,直到時間達到動畫的總時間佳晶,重繪結束,同時滑動完成讼载。
View的重繪距滑動起始會有一個時間間隔轿秧,通過這個時間間隔就能得出當前View的滑動位置,然后通過scrollTo方法完成View的滑動咨堤,這樣每次重繪都會讓View小幅度滑動菇篡,多次就成了彈性滑動。
2.使用動畫
通過動畫的每一幀的到來獲取動畫完成的比例一喘,然后根據這個比例計算View應該滑動的距離驱还。
3.延時策略
通過Handler或postDelayed,或者在線程中通過while和sleep來不斷的發(fā)送消息,在消息中進行View的滑動议蟆。
View的事件分發(fā)機制
1.傳遞規(guī)則
分發(fā)過程即將MotionEvent傳遞到一個具體的View闷沥,由三個重要的
方法共同完成,dispatchTouchEvent咐容,onInterceptTouchEvent舆逃,onTouchEvent。
public booleandispatchTouchEvent(MotionEvent ev)
事件傳遞給當前View一定會執(zhí)行這個方法戳粒,返回結果與View的onTouchEvent和下級View的dispatchTouchEvent有關颖侄,表示是否消耗當前的事件。
public booleanonInterceptTouchEvent(MotionEvent ev)
判斷是否攔截某個事件享郊,在dispatchTouchEvent方法中調用览祖。如果View攔截了某個事件,那么在同一事件序列當中炊琉,該方法不會再被調用
public boolean onTouchEvent(MotionEventev)
處理點擊事件展蒂,判斷是否消耗了事件,如果不消耗苔咪,那在同一事件序列中锰悼,當前View不會再接收到事件。
這三個方法的關系可以用以下偽代碼表示
事件的傳遞規(guī)則:對于根ViewGroup來說团赏,點擊事件首先會傳給它箕般,此時調用它的dispatchTouchEvent方法,然后執(zhí)行onInterceptTouchEvent方法舔清,如果該方法返回true丝里,表示它要攔截該事件,然后事件就會交給ViewGroup處理体谒,然后它的onTouchEvent方法就會被調用杯聚,如果返回false表示它不攔截當前事件,這時事件就會傳遞給它的子元素抒痒,接著調用子元素的dispatchTouchEvent方法幌绍,如此反復直到事件被處理。
監(jiān)聽事件優(yōu)先級OnTouchListener > OnTouchEvent > OnclickListener
當產生點擊事件后故响,總是先傳遞給Activity傀广,再傳遞給Window,最后傳遞給頂級View彩届,然后按照事件的傳遞規(guī)則去分發(fā)事件伪冰。如果所有View都不處理這個事件,那么這個事件最終會傳遞給Activity處理惨缆,Activity的onTouchEvent方法會被調用糜值。
總結
a.同一個時間序列是指從手指接觸屏幕開始,到手指離開屏幕坯墨,中間有一系列的move事件
b.一般一個事件序列只能被一個View攔截消耗寂汇,因為一旦View攔截了某事件,那么同一事件序列內的所有事件都會直接交給它處理捣染,但是View可以將本應自己處理的事件通過onTouchEvent強行傳遞給其它View處理骄瓣。
c.某個View一旦攔截那么該事件序列都只能由它來處理,并且它的onInterceptTouchEvent不會被調用耍攘。即不會再被攔截榕栏。
d.某個View一旦開始處理事件,如果不消耗ACTION_DOWN事件蕾各,即onTouchEvent返回了false扒磁,那么同一事件序列中的其它事件不會再交給它處理,而是由它的父元素處理式曲,即父元素的onTouchEvent會被調用妨托。
e.某個View不消耗除ACTION_DOWN以外的其它事件,那么這個點擊事件會消失吝羞,但父元素的onTouchEvent不會被調用兰伤,并且當前View會持續(xù)收到后續(xù)事件,最終傳遞給Activity處理钧排。
f.ViewGroup默認不攔截任何事件敦腔,即onInterceptTouchEvent默認返回false。
g.View沒有onInterceptTouchEvent方法恨溜,一旦事件傳遞給它符衔,那么它的onTouchEvent方法就會被調用。
h.View的onTouchEvent方法默認都是消耗事件糟袁,除非它是不可點擊的(clickable和longClickable都為false柏腻,View的longClickable默認為false)。
i.事件的傳遞過程是由外向內的系吭,總是由父元素開始五嫂,再傳遞到子View,子View可以通過requestDisallowInterceptTouchEvent干預事件的分發(fā)肯尺,但是ACTION_DOWN除外沃缘。
2.源碼解析
1.Activity的分發(fā)過程
當點擊操作發(fā)生時,事件最先傳遞給當前的Activity则吟,由Activity的dispatchTouchEvent進行事件派發(fā)槐臀,具體是由Activity內的Window來完成的,Window會將事件傳遞給decor view(setContentView設置的View的父容器)氓仲。
2.Window是如何將事件傳遞給ViewGroup的
Window是個抽象類水慨,Window的superDispatchTouchEvent方法也是個抽象方法得糜,PhoneWindow是Window的唯一實現(xiàn)類。
PhoneWindow直接將事件傳給了DecorView晰洒,DecorView是setContentView所設置View的父容器朝抖,所以事件肯定會傳遞給setContentView的View,也叫頂級View谍珊。
3.頂級View對事件的分發(fā)過程
對于根ViewGroup來說治宣,點擊事件首先會傳給它,此時調用它的dispatchTouchEvent方法砌滞,然后執(zhí)行onInterceptTouchEvent方法侮邀,如果該方法返回true,表示它要攔截該事件贝润,然后事件就會交給ViewGroup處理绊茧,然后它的onTouchEvent方法就會被調用,如果返回false表示它不攔截當前事件打掘,這時事件就會傳遞給它的子元素按傅,接著調用子元素的dispatchTouchEvent方法,如此反復直到事件被處理胧卤。
這個方法較長唯绍,我們分段來看
當ViewGroup的子元素成功處理時,mFirstTouchTarget會被賦值枝誊,并指向子元素况芒,即ViewGroup不攔截事件并交給子元素處理時mFirstTouchTarget != null。
當ViewGroup攔截時叶撒,mFirstTouchTarget就為空绝骚,當后續(xù)事件到來時actionMasked != MotionEvent.ACTION_DOWN并且mFirstTouchTarget== null,所以該條件為false祠够,所以ViewGroup的onInterceptTouchEvent不會再被調用压汪,并且同一事件序列中的其它事件都將交給它來處理。
特殊情況:標記位FLAG_DISALLOW_INTERCEPT古瓤,子View可以通過設置requestDisallowInterceptTouchEvent方法止剖,使得ViewGroup無法攔截除了ACTION_DOWN以外的其它事件。如果是ACTION_DOWN事件落君,這個標記位就會被重置穿香。
上面代碼說明了ViewGroup會在ACTION_DOWN事件到來之前重置標記位的狀態(tài)。當ViewGroup不再攔截事件的時候绎速,事件就會下發(fā)至它的子View處理皮获。源碼如下所示
遍歷ViewGroup所有子元素,判斷子元素是否能夠接收點擊事件并且點擊事件是否落在子元素的區(qū)域內纹冤。如果都滿足洒宝,那么事件就交給它來處理购公,在dispatchTransformedTouchEvent方法中有這樣一段代碼。
如果子元素不為空雁歌,直接調用它的dispatchTouchEvent方法宏浩,如果返回true,那么mFirstTouchEvent會被賦值并且跳出循環(huán)将宪。如果返回false,ViewGroup會把事件分發(fā)給下一個元素橡庞。
如果遍歷完所有的元素后時間都沒有被處理较坛,那么有兩種情況,一是ViewGroup沒有子元素扒最,二是子元素處理了點擊事件丑勤,但是dispatchTouchEvent返回了false,一般是因為在TouchEvent中返回了false吧趣,這兩種情況下ViewGroup會自己處理點擊事件法竞。
這里dispatchTransformedTouchEvent方法中第三個參數為null,由之前的代碼分析强挫,它會調用super.dispatchTouchEvent方法岔霸,即交給View處理,接著看View的處理俯渤。
4.View對點擊事件的處理過程呆细。
首先判斷有沒有OnTouchListener,如果OnTouchListener中的onTouch方法返回true八匠,那么onTouchEvent就不會被調用絮爷,所以OnTouchListener優(yōu)先級高于onTouchEvent。
接下來分析onTouchEvent梨树,這里面主要分三個過程: View處于不可用狀態(tài)坑夯,View設置有TouchDelegate,View處于可用狀態(tài)抡四。
a.View不可用
View在不可用狀態(tài)下依然會消耗點擊事件
b.View設置了TouchDelegate
TouchDelegate可以讓某個控件處理比它實際占用空間更大的觸
摸消息柜蜈。具體可以參考官方文檔:TouchDelegate,具體使用:TouchDelegate的使用指巡。
c.View可用
整個過程分為ACTION_UP跨释,ACTION_DOWN,ACTION_CANCEL厌处,ACTION_MOVE四個狀態(tài)鳖谈,我們就最有代表性的ACTION_UP來分析
只要View的CLICKABLE或LONG_CLICKABLE為true,就會消耗這個事件阔涉,并返回true缆娃,然后觸發(fā)performClick方法捷绒,如果View設置了onClick事件,那么onClick就會被調用贯要。
可點擊的View的CLICKABLE為true暖侨,不可點擊的為false
View的LONG_CLICKABLE默認為false
setClickable和setLongClickable可以分別改變其屬性。
setOnClickListener和setOnLongClickListener可以分別將CLICKABLE和LONG_CLICKABLE改為true崇渗。
到這里View的事件體系基本就分析完了字逗,以上所有內容參考自Android開發(fā)藝術探索。
2017.2.16 ?21:39
------END