背景
自己写了一个自定义的ViewGroup的流式布局,但是调用addView方法添加xml中的View的时候报错:
The specified child already has a parent.You must call removeView() on the child's parent first."
探索
二话不说,点源码看一下,顺着addView的流程,最后走到了addViewInner()这个方法,看下代码
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;
}
addInArray(child, index);
// 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);
}
}
}
if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
notifyChildOfDragStart(child);
}
}
从注释中可以看到代码哪里错了,瞬间明白,其实,在Android中,一个View只能属于一个特定的ViewGroup,我自定义了一个ViewGroup,然后又addView()加载xml中的一个view的时候,这个View的外层布局是一个LinearLayout。到了addInnerView的时候。进入if条件语句,抛出异常,崩溃。
if (child.getParent() != null) { //这里抛出了我遇到的错误的异常
throw new IllegalStateException("The specified child already has a parent. " +
"You must call removeView() on the child's parent first.");
}
解决办法
把View的父ViewGroup解除关系,再在自己的ViewGroup添加就好。
if (childView != null) {
ViewGroup parentViewGroup = (ViewGroup) childView.getParent();
if (parentViewGroup != null ) {
parentViewGroup.removeView(childView);
}
}
到这里,这个问题就搞定了。OK!收集小问题,一点一滴。