Android中View跨ViewGroup移动报错问题及解决方案
这个报错的核心原因你已经接近找到,但忽略了一个关键细节:你给LinearLayout开启了android:animateLayoutChanges="true",这会让布局变更(比如移除View)触发动画,而removeView()方法会等到动画结束后才真正完成View与父ViewGroup的解绑。这就导致你调用llLayout.addView(view)时,View的父引用还没被清除,系统就抛出了这个错误。
下面是几种最优解决方案,你可以根据需求选择:
方案1:移除布局动画(最简单,不需要动画时用)
如果你的界面不需要布局变更动画,直接把两个LinearLayout的android:animateLayoutChanges="true"属性删掉即可。这样removeView()会立即执行,View会立刻和原父ViewGroup解绑,之后调用addView()就不会报错了。
方案2:使用removeViewImmediate()强制立即移除(需要快速移动时用)
如果你还想保留其他布局动画,但移动View时不需要等待动画,可以用removeViewImmediate()代替removeView(),这个方法会跳过动画,立即完成View的解绑:
public void addedViewTarget(View view){ llLayout2.removeViewImmediate(view); llLayout.addView(view); }
这个方法的好处是代码改动极小,适合快速解决问题。
方案3:监听布局动画结束再添加View(保留动画效果时用)
如果希望保留移除时的动画效果,同时确保View真正解绑后再添加到目标布局,可以给原ViewGroup的LayoutTransition添加监听器,等待移除动画结束后再执行添加操作:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); llLayout = findViewById(R.id.ly_ll); llLayout2 = findViewById(R.id.ly_ll2); // 给llLayout2添加布局动画监听器 LayoutTransition transition = llLayout2.getLayoutTransition(); transition.addTransitionListener(new LayoutTransition.TransitionListener() { @Override public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {} @Override public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) { // 只处理View移除的动画结束事件 if (transitionType == LayoutTransition.DISAPPEARING && view.getTag() != null && (boolean) view.getTag()) { view.setTag(null); // 清除标记 llLayout.addView(view); } } }); // 给llLayout2的子View设置点击事件 for (int i = 0; i < llLayout2.getChildCount(); i++) { View childView = llLayout2.getChildAt(i); childView.setOnClickListener(v -> { v.setTag(true); // 标记当前View需要被移动 llLayout2.removeView(v); }); } }
这样既保留了移除时的动画效果,又能保证添加操作在View真正解绑后执行,不会触发错误。
另外,顺便优化一下你原代码中点击事件的设置方式,用更简洁的写法(Java 8+支持):
// 替代原来的for循环 llLayout2.forEach(child -> child.setOnClickListener(this::addedViewTarget));
内容的提问来源于stack exchange,提问作者Renaldi

