溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何修改ViewGroup默認(rèn)的順序繪制子View

發(fā)布時(shí)間:2021-10-29 20:33:43 來(lái)源:億速云 閱讀:283 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要講解了“如何修改ViewGroup默認(rèn)的順序繪制子View”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何修改ViewGroup默認(rèn)的順序繪制子View”吧!

TV App 的 Item 處理

修改 View 的繪制順序,在日常開(kāi)發(fā)中,基本用不到。眾多手機(jī)端 App 的 UI 設(shè)計(jì),大部分采用扁平化的設(shè)計(jì)思想,除非是一些很特別的自定義  View,多數(shù)情況下,我們無(wú)需考慮 View 的默認(rèn)繪制順序。

這也很好理解,正常情況下,ViewGroup 中后添加的 View,視覺(jué)上就是應(yīng)該覆蓋在之前的 View 之上。

但是有一個(gè)場(chǎng)景的設(shè)計(jì),很特別,那就是 Android TV App。

在 TV 的設(shè)計(jì)上,因?yàn)樾枰b控器按鍵控制,為了更豐富的視覺(jué)體驗(yàn),是需要額外處理 View 對(duì)焦點(diǎn)狀態(tài)的變化的。

例如:獲取焦點(diǎn)的 ItemView 整個(gè)高亮,放大再加個(gè)陰影,都是很常見(jiàn)的設(shè)計(jì)。

那么這就帶來(lái)一個(gè)問(wèn)題,正常我們使用 RecyclerView 實(shí)現(xiàn)的列表效果,當(dāng) Item 之間的間距過(guò)小時(shí),單個(gè) Item  被放大就會(huì)出現(xiàn)遮蓋的效果。

如何修改ViewGroup默認(rèn)的順序繪制子View

例如上圖所示,一個(gè)很常見(jiàn)的焦點(diǎn)放大高亮的設(shè)計(jì),但卻被后面的 View 遮蓋了。

這樣的情況,如何解決呢?

拍腦袋想,既然是間距太小了,那我們就拉大間距就好了。修改一個(gè)屬性解決一個(gè)需求,設(shè)計(jì)師哭暈在工位上。

不過(guò)確實(shí)有一些設(shè)計(jì)效果,間距足夠,也就不存在遮蓋的現(xiàn)象

但是我們不能只靠改間距解決問(wèn)題,多數(shù)情況下,設(shè)計(jì)師留給我們的間距并不多

既然逃不掉,那就研究一下如何解決。

修改繪制順序原理

修改繪制順序,其實(shí)很簡(jiǎn)單,Android 已經(jīng)為我們留出了擴(kuò)展點(diǎn)。

我們知道,ViewGroup 通過(guò)其成員 mChildren 數(shù)組,存儲(chǔ)子 View。而在 ViewGroup 繪制子 View 的  dispatchDraw() 方法循環(huán)中,并不是直接利用索引從 mChildren 數(shù)組中取值的。

@Override protected void dispatchDraw(Canvas canvas) {   // ...   final ArrayList<View> preorderedList = usingRenderNodeProperties         ? null : buildOrderedChildList();   final boolean customOrder = preorderedList == null         && isChildrenDrawingOrderEnabled();   for (int i = 0; i < childrenCount; i++) {     // ...     final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);     // 并非直接從 mChildren 中獲取     final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);     if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {         more |= drawChild(canvas, child, drawingTime);     }   }   // ... }

可以看到,child 并非是從 mChildren 中直取,而是通過(guò) getAndVerifyPreorderedView() 獲得,它的參數(shù)除了  children 外,還有一個(gè) preorderedList 的 ArrayList,及子 View 的索引。

private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList,         View[] children,         int childIndex) {   final View child;   if (preorderedList != null) {     child = preorderedList.get(childIndex);     if (child == null) {         throw new RuntimeException("Invalid preorderedList contained null child at index "                 + childIndex);     }   } else {     child = children[childIndex];   }   return child; }

在其中,若 preorderedList 不為空,則從其中獲取子 View,反之則還是從 children 中獲取。

回到前面 dispatchDraw() 中,這里使用的 preorderedList 關(guān)鍵列表,來(lái)自  buildOrderedChildList(),在方法中通過(guò) getAndVerifyPreorderedIndex() 獲取對(duì)應(yīng)子 View  的索引,此方法需要一個(gè) Boolean 類型的 customOrder,即表示是否需要自定義順序。

ArrayList<View> buildOrderedChildList() {   // ...   final boolean customOrder = isChildrenDrawingOrderEnabled();   for (int i = 0; i < childrenCount; i++) {     // add next child (in child order) to end of list     final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);     final View nextChild = mChildren[childIndex];     final float currentZ = nextChild.getZ();     // insert ahead of any Views with greater Z     int insertIndex = i;     while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {         insertIndex--;     }     mPreSortedChildren.add(insertIndex, nextChild);   }   return mPreSortedChildren; }

buildOrderedChildList() 的邏輯就是按照 Z 軸調(diào)整 children 順序,Z 軸值相同則參考 customOrder  的配置。

通常 ViewGroup 中的子 View,Z 值一致,所以關(guān)鍵參數(shù)是 customOrder 開(kāi)關(guān)。

從代碼上了解到 customOrder 是通過(guò) isChildrenDrawingOrderEnabled() 方法獲取,與之對(duì)應(yīng)的是  setChildrenDrawingOrderEnabled() 可以設(shè)置 customOrder 的取值。

也就是說(shuō),如果我們要調(diào)整順序,只需 2 步調(diào)整:

調(diào)用 setChildrenDrawingOrderEnable(true) 開(kāi)啟自定義繪制順序

重寫 getChildDrawingOrder() 修改 View 的取值索引

實(shí)例

最后,我們寫個(gè) Demo,重寫 RecycleView 的 getChildDrawingOrder() 方法,來(lái)實(shí)現(xiàn)獲得焦點(diǎn)的 View  最后繪制。

@Override protected int getChildDrawingOrder(int childCount, int i) {   View view = getLayoutManager().getFocusedChild();   if (null == view) {     return super.getChildDrawingOrder(childCount, i);   }   int position = indexOfChild(view);   if (position < 0) {     return super.getChildDrawingOrder(childCount, i);   }   if (i == childCount - 1) {     return position;   }   if (i == position) {     return childCount - 1;   }   return super.getChildDrawingOrder(childCount, i); }

別忘了還需要調(diào)用 setChildrenDrawingOrderEnabled(true) 開(kāi)啟自定義繪制順序。

如何修改ViewGroup默認(rèn)的順序繪制子View

此時(shí),焦點(diǎn)放大時(shí),就不會(huì)被其他 View 遮擋。

感謝各位的閱讀,以上就是“如何修改ViewGroup默認(rèn)的順序繪制子View”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何修改ViewGroup默認(rèn)的順序繪制子View這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI