新人一枚,在學(xué)習(xí)view事件分發(fā)中總是看不懂mFirstTouchTarget newTouchTarget這些touchtarget類對(duì)象,view事件分發(fā)看了其他人的文章3天了,結(jié)合源碼,說一下自己的理解,可能會(huì)有錯(cuò)誤,歡迎指正。手機(jī)上寫的,請(qǐng)見諒!
mfirsttouchtarget是類viewgroup下的一個(gè)touchtarget類私有變量,按理說私有變量無法繼承,無法被子類使用,怎么回事我也不懂,這里應(yīng)該是mfirsttouchtarget這個(gè)變量只有一個(gè)。
首先看一下touchtarget源碼,他是viewgroup下定義的一個(gè)私有靜態(tài)內(nèi)部類,是一個(gè)單鏈表,說明所有viewgroup只有一個(gè)單鏈表
touchtarget與事件分發(fā)有關(guān)系的內(nèi)部變量三個(gè)
public View child; view對(duì)象,為什么起名叫child,后面說
// The combined bit mask of pointer ids for all pointers captured by the target.
public int pointerIdBits;應(yīng)該是多指觸摸情況下給每一次的手指觸摸以id標(biāo)記,太難了不考慮多指觸摸
// The next target in the target list.
public TouchTarget next;touchtarget對(duì)象 指向下一個(gè)鏈表元素,是單鏈表模型的必備要素。
還有幾個(gè)有關(guān)事件分發(fā)的方法
public static TouchTarget obtain(View child, int pointerIdBits) {
final TouchTarget target = new TouchTarget();
target.next = null;?
target.child = child;
target.pointerIdBits = pointerIdBits;
return target; }
obtain是一個(gè)獲取實(shí)例的構(gòu)造方法。
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target;
return target; }
重點(diǎn)來了,mfirsttouchtarget是一個(gè)touchtarget類引用變量。
由方法名可以看出addTouchTarget是一個(gè)向鏈表中添加元素的方法,首先用obtain方法獲取一個(gè)新實(shí)例,再讓這個(gè)新實(shí)例的next指向mfirsttouchtarget指向的實(shí)例,再讓mfirsttouchtarget指向這個(gè)新實(shí)例。假如一開始鏈表里沒有元素,此時(shí)mfirstouchtarget=null,用addtouchtarget加入第一個(gè)元素target1,那target1.next==null,mfirsttouchtarget指向的也是target1。接著加入第2個(gè)元素target2,target2.next==target1,mFirstTouchTarget==target2。所以可以得出一個(gè)結(jié)論,mFirstTouchTarget指向的永遠(yuǎn)是剛加入鏈表的元素也就是表頭,剛加入元素的next指向舊元素。
下面我們?cè)賮砜磘ouchtarget類在view事件分發(fā)中的應(yīng)用,主要是找到addtouchtarget方法,下面是viewgroup的dispatchTouchEvent省略的源碼,viewgroup調(diào)用這個(gè)方法把事件分發(fā)給child,如果不太清楚大家先去看其他人的博客。
首先一開始如果是action_down
if (actionMasked == MotionEvent.ACTION_DOWN) cancelAndClearTouchTargets(ev);重置單鏈表,mFirstTouchTarget=null
resetTouchState();
? ? ? ? ? ? }
if (!canceled && !intercepted){ 接著是一個(gè)大前提如果viewgroup不攔截不取消,進(jìn)行分發(fā)
? ? ? ? ? ? ? ? if (actionMasked == MotionEvent.ACTION_DOWN
? ? ? ? ? ? ? ? ? ? ? ? || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
? ? ? ? ? ? ? ? ? ? ? ? || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {里面還有一個(gè)前提是action_down的時(shí)候
? ? ? ? ? ? ? ? ? ? final int childrenCount = mChildrenCount;
? ? ? ? ? ? ? ? ? ? if (newTouchTarget == null && childrenCount != 0) {
? ? ? ? ? ? ? ? ? ? ? ? final float x = ev.getX(actionIndex);
? ? ? ? ? ? ? ? ? ? ? ? final float y = ev.getY(actionIndex);
? ? ? ? ? ? ? ? ? ? ? ? final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {對(duì)這個(gè)viewgroup的children進(jìn)行遍歷。
if (!canViewReceivePointerEvents(child)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? || !isTransformedTouchPointInView(x, y, child, null)) {如果觸摸點(diǎn)(x,y)的坐標(biāo)在child范圍外,跳出,對(duì)下一個(gè)child進(jìn)行檢驗(yàn)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
走到這部,說明在child范圍內(nèi)
? ? ? ? ? ? ? ? ? ? ? ? ? ? newTouchTarget = getTouchTarget(child);
getTouchTarget通過查詢child,返回包含這個(gè)child的TouchTarget對(duì)象
? ? ? ? ? ? ? ? ? ? ? ? ? ? if (newTouchTarget != null) {如果找到,說明newTouchTarget對(duì)象在單鏈表中,可以直接跳出循環(huán),我的想法是單鏈表形成就是加入接收事件并處理的child,現(xiàn)在找到了說明這個(gè)child上一次處理了action_down,把其他事件都給他,比如其他手指point_down,前面if判斷有。
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;}
如果newtouchtarget為零,說明沒有人處理過這個(gè)事件
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {view child調(diào)用child.dispatchTouchEvent進(jìn)行遞歸分發(fā),如果child還有child,那么再次分發(fā)child.child.dispatchtouchevent,如果有child處理了事件,返回true,執(zhí)行下列語句。
newTouchTarget = addTouchTarget(child, idBitsToAssign);在這里向鏈表中添加touchtarget(child,idBitsToAssign)對(duì)象,注意因?yàn)槭沁f歸引用所以先加入鏈表的是子view,也就是最低端的子view在鏈表最下層,到最后newTouchTarget==mfirstTouchTarget指向最晚加入的child,比如說如下圖,最開始的viewgroup是DecorView,它調(diào)用dispatchtouchevent,mfirstTouchTarget在decorview這里,它指向的就是decorview的子view,viewGroup1。我們前面說為什么touchtarget的view變量用child作為名字,因?yàn)閙firsttouchtarget指向的就是調(diào)用dispatchtouchevent方法的父view方法子view。
如下圖經(jīng)過層層遞歸,如果最下面的view21能處理action_down,就會(huì)形成一個(gè)viewgroup1 viewgroup2 view21形成的鏈表,這個(gè)鏈表所有viewgroup類共享,實(shí)際處理的view21在最下面,它是第一個(gè)被viewgroup2加入鏈表的。
后來發(fā)現(xiàn)這里有個(gè)問題decorview調(diào)用的是父視圖framelayout的dispatchtouchevent,也就是說Decorview上面還有一層,所以mFirstTouchTarget指向的是decorview

以上就是我對(duì)mfirsttouchtarget的理解,由于手機(jī)所做,比較難看,可能有些地方不對(duì),求指導(dǎo),我以后也會(huì)多寫這種費(fèi)眼費(fèi)腦的垃圾文章的,謝謝大家滋持!
?