Android中整個的View的組裝是採用組合模式。 ViewGroup就相當與樹根,各種Layout就相當於枝幹,各種子View,就相當於樹葉。 至於View類。我們就當它是個種子吧。哈哈! ViewGroup屬於樹根,可以生長數很多枝幹(繼承自定義Layout)而枝幹上有可以長出很多葉子(Tex ...
Android中整個的View的組裝是採用組合模式。
ViewGroup就相當與樹根,各種Layout就相當於枝幹,各種子View,就相當於樹葉。
至於View類。我們就當它是個種子吧。哈哈!
ViewGroup屬於樹根,可以生長數很多枝幹(繼承自定義Layout)而枝幹上有可以長出很多葉子(TextView,ImageVIew......)
好,閑話少敘,接下來步入正題!
首先,關於View的操作方法,被定義在一個叫做ViewManager的介面中,介面中還有兩個方法,分別是移除和更新,這次主要分析addView
public interface ViewManager { /** * Assign the passed LayoutParams to the passed View and add the view to the window. * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view) }
addView在ViewGroup中實現,接下來貼出代碼進行分析
public void addView(View child, LayoutParams params) { addView(child, -1, params); }
這個方法中調用了自身的addView方法,並且多傳遞了一個-1,這個-1是View的索引,要插入的位置
public void addView(View child, int index, LayoutParams params) { if (DBG) { System.out.println(this + " addView"); } if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } // addViewInner() will call child.requestLayout() when setting the new LayoutParams // therefore, we call requestLayout() on ourselves before, so that the child's request // will be blocked at our level requestLayout(); invalidate(true); addViewInner(child, index, params, false); }
這個方法先是重繪了一下佈局,然後調用了addViewInner(child, index, params, false);方法,來把View插入到相應的位置
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { if (mTransition != null) { // Don't prevent other add transitions from completing, but cancel remove // transitions to let them complete the process before we add to the container mTransition.cancel(LayoutTransition.DISAPPEARING); } if (child.getParent() != null) { throw new IllegalStateException("The specified child already has a parent. " + "You must call removeView() on the child's parent first."); } if (mTransition != null) { mTransition.addChild(this, child); } if (!checkLayoutParams(params)) { params = generateLayoutParams(params); } if (preventRequestLayout) { child.mLayoutParams = params; } else { child.setLayoutParams(params); } if (index < 0) { index = mChildrenCount; } //ViewGroup用一個View類型的數組去維護下邊的子view,這個方法就是把view添加到響應的位置上 addInArray(child, index); //綁定插入的view的父容器為當前group // tell our children if (preventRequestLayout) { child.assignParent(this); } else { child.mParent = this; } if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } AttachInfo ai = mAttachInfo; if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { needGlobalAttributesUpdate(true); } ai.mKeepScreenOn = lastKeepOn; } if (child.isLayoutDirectionInherited()) { child.resetRtlProperties(); } dispatchViewAdded(child); if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; } if (child.hasTransientState()) { childHasTransientStateChanged(child, true); } if (child.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } if (mTransientIndices != null) { final int transientCount = mTransientIndices.size(); for (int i = 0; i < transientCount; ++i) { final int oldIndex = mTransientIndices.get(i); if (index <= oldIndex) { mTransientIndices.set(i, oldIndex + 1); } } } }
這個方法先檢查一下LayoutParams,看看插入的該view是否存在長寬,如果沒有就生成一個預設的LayoutParams
然後判斷index是否小於0,小於0就賦值為當前容器中的個數,代表插入到最後一項
隨後就調用addInArray方法,來插入View到當前ViewGroup中,插入完成後給該view綁定一下父容器(getParent的值)
隨後就是一些焦點,監聽的分發,我們仔細分析一下插入方法就好了,就是addInArray
private void addInArray(View child, int index) { View[] children = mChildren; final int count = mChildrenCount; final int size = children.length; if (index == count) { if (size == count) { mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; System.arraycopy(children, 0, mChildren, 0, size); children = mChildren; } children[mChildrenCount++] = child; } else if (index < count) { if (size == count) { mChildren = new View[size + ARRAY_CAPACITY_INCREMENT]; System.arraycopy(children, 0, mChildren, 0, index); System.arraycopy(children, index, mChildren, index + 1, count - index); children = mChildren; } else { System.arraycopy(children, index, children, index + 1, count - index); } children[index] = child; mChildrenCount++; if (mLastTouchDownIndex >= index) { mLastTouchDownIndex++; } } else { throw new IndexOutOfBoundsException("index=" + index + " count=" + count); } }
mChildren是ViewGroup中的一個View類型的數組,裡邊存放了該Group下的所有子View
ARRAY_CAPACITY_INCREMENT這個常量的值是12
首先判斷一下mChildren的位置還是否充足,不充足就繼續擴充12個位置出來,copy源數組內容進到新數組裡,然後再把本次要添加的view放到最後
如果index比count小,說明是插入操作,也是先判斷位置是否充足,不充足就擴充並且copy到index處,然後在把剩下的copy到index+1到末尾
這樣就把index位置空了出來。就完成了插入操作
插入完成後,系統會重繪界面,你就可以看到你插入的view了。
addView這個方法就分析完了,有什麼疑問就可以指出來,錯誤也一樣。共同學習共同進步。