首先在子元素的父元素上调用removeView()方法。

145

首先简单介绍一下背景:

我有一个位于scrollview内的布局。刚开始,当用户在屏幕上滚动时,scrollview会滚动。但是,在滚动了一定量后,我想禁用scrollview上的滚动并将“滚动焦点”移动到子布局内部的webview上。这样,scrollview就不会滚动了,而所有的滚动事件都传递给它内部的webview。

那么,解决方案是,当滚动阈值达到时,我将子布局从scrollview中移除并放入scrollview的父布局中(并将scrollview设置为不可见)。

// Remove the child view from the scroll view
scrollView.removeView(scrollChildLayout);

// Get scroll view out of the way
scrollView.setVisibility(View.GONE);

// Put the child view into scrollview's parent view
parentLayout.addView(scrollChildLayout);

总体思路:(->表示包含)

之前:parentlayout -> scrollview -> scrollChildLayout

之后:parentLayout -> scrollChildLayout

上述代码给了我这个异常:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
           at android.view.ViewGroup.addViewInner(ViewGroup.java:1976)
           at android.view.ViewGroup.addView(ViewGroup.java:1871)
           at android.view.ViewGroup.addView(ViewGroup.java:1828)
           at android.view.ViewGroup.addView(ViewGroup.java:1808)

你知道发生了什么吗?我明显在父级上调用了removeView。

11个回答

294

解决方案:

((ViewGroup)scrollChildLayout.getParent()).removeView(scrollChildLayout);
//scrollView.removeView(scrollChildLayout);

使用子元素来获取对父元素的引用。将父元素强制转换为ViewGroup,以便您可以访问removeView方法并使用它。

感谢@Dongshengcn提供的解决方案


1
解决方案是正确的,但为什么“scollView.removeView(scrollChildLayout)”不起作用呢?这两行不应该是一样的吗? - andrea.rinaldi
@andrea.spot,看起来scrollView.removeView(scrollChildLayout)试图从scrollView中移除子视图,而不是将scrollView本身从其父ViewGroup中移除。 - Dmitry Gryazin
1
@user25 我知道这可能对你来说有点晚了,但空指针异常是由于父级为空造成的,这意味着视图未附加到任何父级。由于一开始没有父级,因此没有要移除的内容。 if (mAdView.getParent()!=null) ((ViewGroup) mAdView.getParent()).removeView(mAdView); - Tarek
@kasgoku,你能帮我一下吗?我正在尝试在按钮点击时在相对布局中显示一个片段。但是它显示错误:“指定的子项已经有一个父项。您必须首先调用removeView()来删除子项的父项。”如何解决这个问题? - im07
我在我的聊天应用程序中也遇到了这个错误。我正在尝试解决这个问题。那个图标已经被删除了,但是我需要再次使用该图标来上传图片。(https://stackoverflow.com/q/58117428/10971384) 这是我的问题链接。 - Thangapandi
顺便提一下,我有碎片但是没有滚动视图,那么我该如何解决获取相同错误的问题。 - Pankaj Gadhiya

22

首先尝试从其父视图中移除scrollChildLayout?

scrollview.removeView(scrollChildLayout)

或者从父视图中删除所有子视图,然后再重新添加它们。

scrollview.removeAllViews()

我已经在上面的代码中调用了removeView。 removeAllViews()也不起作用。 - kasgoku
你确定你正在调用removeView()/removeAllViews()的父级视图吗?我正在做非常类似的事情,而且一直在正常工作。 - dongshengcn
4
您可以通过查看:view.getParent() 来查看其父级。http://developer.android.com/reference/android/view/View.html#getParent() - dongshengcn
@dongshengcn,你能帮我一下吗?我正在尝试在按钮点击时在相对布局中显示一个片段。但是它显示错误:“指定的子项已经有一个父项。您必须首先调用removeView()来删除子项的父项。”如何解决这个问题? - im07

20

在活动中的onCreate方法中,或者在片段中的onCreateView方法中。

 if (view != null) {
    ViewGroup parent = (ViewGroup) view.getParent();
    if (parent != null) {
        parent.removeView(view);
    }
}
try {
    view = inflater.inflate(R.layout.fragment_main, container, false);
} catch (InflateException e) {

}

16

好的,可能有点多虑,但我建议:

  final android.view.ViewParent parent = view.getParent ();

  if (parent instanceof android.view.ViewManager)
  {
     final android.view.ViewManager viewManager = (android.view.ViewManager) parent;

     viewManager.removeView (view);
  } // if

没有使用 instanceof 进行转换似乎是不正确的。(感谢 IntelliJ IDEA 告诉我)removeViewViewManager 接口的一部分。当有完全适合的接口可用时,不应该将其强制转换为具体的类。


5

Kotlin 解决方案

Kotlin通过 as? 简化了父类型转换,如果左侧为null或者转换失败则返回null。

(childView.parent as? ViewGroup)?.removeView(childView)

Kotlin扩展解决方案

如果你想要进一步简化这个过程,你可以添加这个扩展。

childView.removeSelf()

fun View?.removeSelf() {
    this ?: return
    val parentView = parent as? ViewGroup ?: return
    parentView.removeView(this)
}

如果这个View为空,父视图为空或者父视图不是一个ViewGroup,则它将安全地什么也不做。

注意:如果您还想通过父级安全地删除子视图,请添加以下内容:

fun ViewGroup.removeViewSafe(toRemove: View) {
    if (contains(toRemove)) removeView(toRemove)
}

这个答案没有提供任何实际解释问题的内容。 - dephinera

3

你只需要post()一个Runnable来执行addView()方法。


没起作用。这是我尝试的代码:scrollView.removeView(scrollChildLayout); scrollView.setVisibility(View.GONE); parentLayout.post(new Runnable() { @Override public void run() { parentLayout.addView(scrollChildLayout); } }); - kasgoku

1
在我的情况下,我有一个BaseFragment,所有其他片段都继承自它。
所以我的解决方案是在OnDestroyView()方法中添加这些行。
@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    if (mRootView == null)
    {
        mRootView = (inflater == null ? getActivity().getLayoutInflater() : inflater).inflate(mContentViewResourceId, container, false);
    }
....////
}

@Override
public void onDestroyView()
{
    if (mRootView != null)
    {
        ViewGroup parentViewGroup = (ViewGroup) mRootView.getParent();

        if (parentViewGroup != null)
        {
            parentViewGroup.removeAllViews();
        }
    }

    super.onDestroyView();
}

你尝试过选择下一个片段并太快地按返回键吗? - iamthevoid

1
我调用了parentView.removeView(childView),但childView仍然显示。最终我意识到某个方法被触发了两次,导致childView被添加到parentView两次。
因此,在添加视图之前和之后使用parentView.getChildCount()来确定父级有多少个子项。如果子项被添加了太多次,则最顶部的子项将被删除,而复制的childView仍然存在 —— 这看起来像是removeView正在工作,即使它并没有。
另外,您不应该使用View.GONE来删除视图。如果它真的被删除了,那么您就不需要隐藏它,否则它仍然存在,只是从自己的视线中隐藏了 :(

0

你也可以通过检查View的indexOfView方法来实现:

如果indexOfView方法返回-1,则可以使用:

ViewGroup的detachViewFromParent(v);

接着是

ViewGroup的removeDetachedView(v, true/false);


0
我出错的原因是没有实例化动态布局并向其中添加子项,因此出现了这个错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接