最近在項目終於到了View.bringToFront()方法,簡單看了下其源碼,在這兒總結一下。
bringToFront方法在SDK中的說明是“Change the view's z order in the tree, so it's on top of other sibling views”,翻譯過來就是改變view的z軸,使其處在他父view的頂端。關於bringToFront的實現,網上有很多資料介紹,大體來說就是將這個view從父view中移除,然后再加入父view的頂端。具體實現如何呢?
bringToFront的具體實現要參看ViewGroup中的bringChildToFront方法。代碼很簡單,如下:
public void bringChildToFront(View child) { int index = indexOfChild(child); if (index >= 0) { removeFromArray(index); addInArray(child, mChildrenCount); child.mParent = this; } }
分兩步,首先remove,然后add,實際上ViewGroup維持了一個View數組,addInArray方法會把這個child加入到數組最末端,在draw的時候,將依次畫數組中的child,最后一個自然就放到了頂端。
removeFromArray方法中有一段奇怪的代碼:
if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) { children[index].mParent = null; }
mTransitioningViews官方解釋如下:
The set of views that are currently being transitioned. This list is used to track views being removed that should not actually be removed from the parent yet because they are being animated.
正如上述,當某個child正在做動畫的時候(這里指android.app.Fragment和android.animation.LayoutTransition移除view的動畫),還不能刪除這個child,應該等到動畫結束,所以在ViewGroup中暫時保留這個child,直到動畫真正結束后再真正刪除。ViewGroup有兩個成對出現的方法:startViewTransition和endViewTransition。在startViewTransition方法中將child加入mTransitioningViews中,在endViewTransition中最終執行view.dispatchDetachedFromWindow(),並在函數最后調用invalidate()。
值得一提的是,在removeView的時候,如果當前child在mTransitioningViews中,ViewGroup並不會執行view.dispatchDetachedFromWindow(),也不會設置當前view的mParent為空。
沒想到分析bringToFront方法竟然還意外發現一個mTransitioningViews,由此可以看到一個潛在的問題,如果我們在執行LayoutTransition的DISAPPEARING動畫同時removeView,這時子view還並未刪除,如果直接將子view加入其它ViewGroup中則會報錯“The specified child already has a parent. You must call removeView() on the child's parent first.” 因為此時這個view還未從上一個ViewGroup中刪除。