日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android viewgroup点击变色,Android ViewGroup事件分发

發布時間:2023/12/2 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android viewgroup点击变色,Android ViewGroup事件分发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上篇文章已經分析了Android的Touch事件分發。如果沒看的建議先看一下。Android View的Touch事件分發。

接下來我們開始寫幾種場景,得出一個初步的執行順序,然后我們按照這個順序開始分析。

首先我們自定義一個ViewGroup和一個View,然后重寫相關事件進行打印:

場景一:正常返回super,TouchView設置click和onTouchListener事件(onTouch返回false)

TouchViewGroup.png

TouchView.png

布局.png

TouchView設置事件.png

這時候我們點擊一下TouchView,觸發事件:

點擊一下.png

可以看到觸發的DOWN MOVE UP事件順序都為:

ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch -> View.onTouchEven

只是在UP事件的時候最后多了一個click事件。

場景二:在場景一的基礎上取消TouchView的onClick事件

TouchView取消click事件.png

這時候發現除了,執行的順序變為了:

ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> View.dispatchTouchEvent -> View.onTouch -> View.onTouchEven->ViewGroup.onTouchEven

并且只有DOWN事件,其他事件就沒有了。

場景三:在場景二的基礎上TouchViewGroup的onInterceptTouchEvent里面返回true

這個時候就只有DOWN事件,并且順序為:

ViewGroup.dispatchTouchEvent -> ViewGroup.onInterceptTouchEvent -> ViewGroup.onTouchEvent

接下來我們通過源碼來分析:

首先從ViewGroup的dispatchTouchEvent入手

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

//...

boolean handled = false;

//...

//1.取消之前的手勢

// Handle an initial down.

if (actionMasked == MotionEvent.ACTION_DOWN) {

// Throw away all previous state when starting a new touch gesture.

// The framework may have dropped the up or cancel event for the previous gesture

// due to an app switch, ANR, or some other state change.

cancelAndClearTouchTargets(ev);

resetTouchState();

}

//2.判斷是否攔截

// Check for interception.

final boolean intercepted;

if (actionMasked == MotionEvent.ACTION_DOWN

|| mFirstTouchTarget != null) { //DOWN

//父類是否攔截 getParent().requestDisallowInterceptTouchEvent();來改變值

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

if (!disallowIntercept) {

intercepted = onInterceptTouchEvent(ev);

ev.setAction(action); // restore action in case it was changed

} else {

intercepted = false;

}

} else {

// There are no touch targets and this action is not an initial down

// so this view group continues to intercept touches.

intercepted = true;

}

//....

//3.0 如果是不取消不攔截為down,并且dispatchTransformedTouchEvent返回為true的時候會為 mFirstTouchTarget賦值

// Check for cancelation.

final boolean canceled = resetCancelNextUpFlag(this)

|| actionMasked == MotionEvent.ACTION_CANCEL;

// Update list of touch targets for pointer down, if needed.

final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;

TouchTarget newTouchTarget = null;

boolean alreadyDispatchedToNewTouchTarget = false;

//3.1 如果不取消并且不攔截的情況下,

if (!canceled && !intercepted) {

if (actionMasked == MotionEvent.ACTION_DOWN

|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)

|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {// 3.2 DOWN的時候

//...

if (newTouchTarget == null && childrenCount != 0) {

//...

final View[] children = mChildren;

for (int i = childrenCount - 1; i >= 0; i--) {//3.3 反序for循環,為了先拿到上層的view

//...

//3.4 拿到child

final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);

//...

//3.5 根據child給newTouchTarget賦值 DOWN的時候因為 mFirstTouchTarget==null 所以進不去 返回的是null

newTouchTarget = getTouchTarget(child);

}

//...

//3.6. 執行操作 是執行自己的dispatchTouchEvent還是child的dispatchTouchEvent

if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

//...

//3.7 子View如果返回true添加一個newTouchTarget 并且為mFirstTouchTarget賦值

newTouchTarget = addTouchTarget(child, idBitsToAssign);

//....

}

}

}

}

//...

// Dispatch to touch targets.

if (mFirstTouchTarget == null) {//執行自身的dispatchTouchEvent

// No touch targets so treat this as an ordinary view.

handled = dispatchTransformedTouchEvent(ev, canceled, null,

TouchTarget.ALL_POINTER_IDS);

} else {// mFirstTouchTarget已經賦值

// Dispatch to touch targets, excluding the new touch target if we already

// dispatched to it. Cancel touch targets if necessary.

TouchTarget predecessor = null;

TouchTarget target = mFirstTouchTarget;

while (target != null) {

final TouchTarget next = target.next;

if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {//執行完3.7操作的

handled = true;

} else {

final boolean cancelChild = resetCancelNextUpFlag(target.child)

|| intercepted;

if (dispatchTransformedTouchEvent(ev, cancelChild,

target.child, target.pointerIdBits)) {

handled = true;

}

if (cancelChild) {

if (predecessor == null) {

mFirstTouchTarget = next;

} else {

predecessor.next = next;

}

target.recycle();

target = next;

continue;

}

}

predecessor = target;

target = next;

}

}

return handled;

}

/**

* Cancels and clears all touch targets.

*/

private void cancelAndClearTouchTargets(MotionEvent event) {

if (mFirstTouchTarget != null) {

boolean syntheticEvent = false;

if (event == null) {

final long now = SystemClock.uptimeMillis();

event = MotionEvent.obtain(now, now,

MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);

event.setSource(InputDevice.SOURCE_TOUCHSCREEN);

syntheticEvent = true;

}

for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {

resetCancelNextUpFlag(target.child);

dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);

}

clearTouchTargets();

if (syntheticEvent) {

event.recycle();

}

}

}

//清楚所有的TouchTarget

/**

* Clears all touch targets.

*/

private void clearTouchTargets() {

TouchTarget target = mFirstTouchTarget;

if (target != null) {

do {

TouchTarget next = target.next;

target.recycle();

target = next;

} while (target != null);

mFirstTouchTarget = null;

}

}

//根據childVie得到TouchTarget

/**

* Gets the touch target for specified child view.

* Returns null if not found.

*/

private TouchTarget getTouchTarget(@NonNull View child) {

// DOWN的時候因為 mFirstTouchTarget==null 所以進不去 返回的是null

for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {

if (target.child == child) {

return target;

}

}

return null;

}

/**

* Transforms a motion event into the coordinate space of a particular child view,

* filters out irrelevant pointer ids, and overrides its action if necessary.

* If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.

*/

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,

View child, int desiredPointerIdBits) {

//偽代碼

final boolean handled;

if (child == null) {//執行View.dispatchTouchEvent 也就是自己的dispatchTouchEvent

handled = super.dispatchTouchEvent(event);

} else {//執行child的dispatchTouchEvent

handled = child.dispatchTouchEvent(event);

}

return handled;

}

//添加TouchTarget 并且給mFirstTouchTarget賦值

/**

* Adds a touch target for specified child to the beginning of the list.

* Assumes the target child is not already present.

*/

private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {

final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);

target.next = mFirstTouchTarget;

mFirstTouchTarget = target;

return target;

}

當DOWN的時候,從注釋和方法名可以看出,會調用cancelAndClearTouchTargets,然后在調用clearTouchTargets使mFirstTouchTarget = null用來廢棄上一次的觸摸手勢。

接著判斷父類需不需要攔截,先通過(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0來判斷,在這里可以通過getParent().requestDisallowInterceptTouchEvent(boolean disallowIntercept)來改變值,如果上面為判斷為false再通過onInterceptTouchEvent的返回值來確定,這個函數默認情況下返回false。

檢測是否為取消事件,如果不是取消、不攔截并且為 DOWN事件的時候,就會對childView一個反序的for循環來遍歷,并且執行dispatchTransformedTouchEvent操作,這個操作用來執行dispatchTouchEvent,如果childView是null的話將執行View.dispatchTouchEvent,也就是自己的dispatchTouchEvent,反之執行childView的dispatchTouchEvent,如果執行dispatchTransformedTouchEvent返回的值是true那么將會調用addTouchTarget()為這個childView生成一個TouchTarget并且執行mFirstTouchTarget = target將之賦值于mFirstTouchTarget ,然后跳出for循環遍歷。

判斷操作,首先判斷mFirstTouchTarget是否為null,如果是DOWN事件,不攔截不取消并且dispatchTransformedTouchEvent返回了true,那么將會不進入這個判斷,如果不是,那么將會在這執行自身的dispatchTouchEvent函數并且將返回值賦于handled返回。進入else語句,在里面將其mFirstTouchTarget進行next遍歷,里面的if語句則是DOWN事件下的dispatchTransformedTouchEvent返回true的情況,直接將其賦值,然后返回,里面的else語句則是,調用dispatchTransformedTouchEvent,然后將其返回值返回。

到這里,ViewGroups事件分發源碼的流程就分析了,我們根據這個來說說上面的場景。

場景一:我們在TouchViewGroup的dispatchTouchEvent正常返回super,DOWN事件先觸發TouchViewGroup的dispatchTouchEvent,然后就執行onInterceptTouchEvent是否攔截,onInterceptTouchEvent返回的是super,也就是false,所以就會通過dispatchTransformedTouchEvent來執行TouchView的dispatchTouchEvent,后面就是View的Touch事件分發了,View流程將會按照dispatchTouchEvent->onTouchListener - > onTouchEvent的順序執行,因為設置了點擊事件,所以在這里就返回了true,這個時候就會通過addTouchTarget()給mFirstTouchTarget賦值,下面就直接返回了true。然后在MOVE和UP事件的時候,也是首先執行dispatchTouchEvent,調用super然后調用onInterceptTouchEvent詢問是否攔截,還是false,但是這里因為不是DOWN事件,所以就不會進入判斷對其childView反遍歷,因為在DOWN的時候mFirstTouchTarget賦值了,所以這時候進入第4步的else語句里面,這時候就對其遍歷執行dispatchTransformedTouchEvent,也就是dispatchTouchEvent,然后將其返回。

場景2:我們取消了點擊事件,那么在DOWN的時候就不會給mFirstTouchTarget賦值,這個時候將會進入第4步的if判斷里面,直接調用dispatchTransformedTouchEvent,所以事件就不會有攔截,最終返回false,所以后續將不會接受到任何事件

場景3:我們在TouchViewGroup的時候是在onInterceptTouchEvent返回true,所以我們intercepted=true,這時候就不會給mFirstTouchTarget賦值,這個時候就調用自身的dispatchTransformedTouchEvent,同樣的返回false,后續將不會接受到事件。

通過源碼的角度我們也知道了為什么會這么執行,初步有點模糊,我們需要通過項目慢慢的來完善對它的認知。希望對大家有所幫助。

總結

以上是生活随笔為你收集整理的android viewgroup点击变色,Android ViewGroup事件分发的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。