【问题标题】:Error using the RecyclerView: The specified child already has a parent使用 RecyclerView 时出错:指定的子节点已经有父节点
【发布时间】:2014-10-27 20:39:53
【问题描述】:

我正在尝试使用新的 RecyvlerView 适配器创建 listView。我遵循了 android 开发人员资源上的确切指南。但这给了我一个奇怪的错误:指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()。 我有最新的 SDK。我还在 gradle 中定义了依赖项。

我的活动(主要活动):

public class MyActivity extends Activity {

    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(new String[]{"Zain", "Nadeem"});

        mRecyclerView.setAdapter(mAdapter);
    }
}

我的适配器:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private String[] mDataset;    
    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // create a new view
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());  

        View v = inflater.inflate(R.layout.my_text_view, parent, true);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element    
        holder.mTextView.setText(mDataset[position]);
    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(View v) {
            super(v);
            mTextView = (TextView) v.findViewById(R.id.item_title);
        }
    }
}

my_text_view.xml(列表项的布局):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="80dp"

    >
    <!-- title -->
    <TextView
        android:id="@+id/item_title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/darker_gray"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:textSize="22dp" />

</RelativeLayout>

activity_my.xml(主要活动):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    android:background="@android:color/holo_orange_light"
    >
    <!-- A CardView that contains a TextView -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MyActivity"/>
    </LinearLayout>

这是 logcat 的输出:

10-28 01:20:30.287  22729-22729/osman.zaingz.com.lollipop E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: osman.zaingz.com.lollipop, PID: 22729
    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:3562)
            at android.view.ViewGroup.addView(ViewGroup.java:3415)
            at android.view.ViewGroup.addView(ViewGroup.java:3360)
            at android.support.v7.widget.RecyclerView$4.addView(RecyclerView.java:322)
            at android.support.v7.widget.ChildHelper.addView(ChildHelper.java:79)
            at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:4845)
            at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4803)
            at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4791)
            at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1316)
            at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
            at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:522)
            at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1918)
            at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2155)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
            at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1660)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1436)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:374)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
            at android.view.View.layout(View.java:14817)
            at android.view.ViewGroup.layout(ViewGroup.java:4631)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1983)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1740)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:996)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5600)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
            at android.view.Choreographer.doCallbacks(Choreographer.java:574)
            at android.view.Choreographer.doFrame(Choreographer.java:544)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)

【问题讨论】:

  • 使用inflater.inflate(R.layout.my_text_view, parent, false);
  • 行视图 v = inflater.inflate(R.layout.my_text_view, parent, true);将视图附加到父级,使用 View v = inflater.inflate(R.layout.my_text_view, parent, false);而是
  • @panini 你是魔术师。我已经尝试过很多次了。但我现在它只是工作。非常感谢您的快速回复
  • 我也有相同的代码并使用 View v = inflater.inflate(R.layout.my_text_view, parent, false),但仍然得到相同的异常。
  • 在我的情况下,我得到了错误,因为我给 ViewHolder 的视图不是我上面提到的 inflate(...) 创建的条目的“根”,而是我的条目的内部 TextView .

标签: android listview android-recyclerview


【解决方案1】:

膨胀时,您不应将视图附加到其父级。 你写道:

View v = inflater.inflate(R.layout.my_text_view, parent, true);

应该是:

View v = inflater.inflate(R.layout.my_text_view, parent, false);

【讨论】:

  • 另外你不能使用 View v = inflater.inflate(R.layout.my_text_view, parent);因为它默认为 true。
  • 一般来说,为什么我们传递给 ViewHolder 的 View 不应该有父级?这里的缓存究竟是如何发生的?
  • 因为它会被添加到 recyclerview 并且视图一次只能有一个父视图
【解决方案2】:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="80dp">

<TextView
    android:id="@+id/item_title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="@android:color/darker_gray"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:layout_marginTop="8dp"
    android:textSize="22dp" />

</RelativeLayout>

RelativeLayout 这是您的 TextView 的父级,ID 为 item_title。 然后,当 RecyclerView 尝试添加具有父级的 TextView 时,它会抛出异常。

试试这个代码:

public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        Log.d(TAG, "onCreateViewHolder");
        RelativeLayout v = (RelativeLayout)LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item, parent, false);
        // set the view's size, margins, paddings and layout parameters
        View view = v.findViewById(R.id.text1);
        v.removeView(view);
        ViewHolder vh = new ViewHolder(view);
        return vh;
    }

【讨论】:

  • v.removeView() 是不需要的!只需将 v 本身传递给 ViewHolder 构造函数,而不是 view。并在 ViewHolder 构造函数中调用 findViewById()
  • 使用parent.getContext() 膨胀视图为我解决了这个问题。谢谢!
  • 很好的解释:“RelativeLayout 这是您的 TextView 的父项,ID 为 item_title。然后当 RecyclerView 尝试添加具有父项的 TextView 时,它会抛出异常。”
【解决方案3】:

如果您快速滚动您的回收站视图,也会发生这种情况。 我的案例是使用 DiffUtils 插入/移动/...项目并调用 TransitionManager.beginDelayedTransition(view) 在 recyclerview 动画结束之前,在我的代码中某处的根布局(以及 recyclerview)上

【讨论】:

  • 您是否碰巧找到了解决方案?
  • 哇,这就是解决我的特定问题的原因。伟大的发现,朋友!你怎么知道要检查这个?如果我独自一人,那将永远带我去......
  • 我使用transition.excludeChildren(myRecycler, true) 解决了这个问题。
  • @Westy92 我正在使用TransitionManager.beginDelayedTransition(view)。我应该在哪个对象上调用excludeChildren()
  • @HudiIlfeld TransitionManager.beginDelayedTransition() 采用第二个(可选)参数,即自定义 Transition。在您使用的自定义 Transition 上调用 excludeChildren。默认Transition 类型为AutoTransition
【解决方案4】:

也许有点晚了,但我遇到了同样的问题(recyclerview-v7:26.1.0),原因是我试图(通过 scrollToPosition() 方法)将列表滚动到当前可见的位置. 在滚动之前添加位置检查(仅在不可见位置滚动)后,问题得到解决

【讨论】:

    【解决方案5】:

    我通过删除 `TransitionManager.beginDelayedTransition(); 解决了这个问题

    【讨论】:

      【解决方案6】:

      在我们的例子中,尝试执行自定义 TransitionsexcludeChildren 方法时出现的问题。

      这是我们迄今为止解决问题的方法

      在 Kotlin 中:TransitionHelpers.kt

      // single child
      private fun useAutoTransition(childView: View, 
                                     excludeChildView: Boolean = true
      ) = AutoTransition().apply {
          excludeChildren(childView, excludeChildView)
          // apply any other method you need here
      }
      
      private fun useAutoTransition(@IdRes childViewId: Int, 
                                     excludeChildView: Boolean = true
      ) = AutoTransition().apply {
          excludeChildren(childViewId, excludeChildView)
          // apply any other method you need here
      }
      
      // multiple child
      fun useAutoTransitions(excludeChildView: Boolean, @IdRes vararg childViewIds: Int) {
              val auto = AutoTransition();
      
              for (id in childViewIds) {
                 auto.excludeChildren(id, excludeChildView)
              }
      
              return auto;
          }
      
      fun useAutoTransitions(excludeChildView: Boolean, vararg childViews: View) {
              val auto = AutoTransition();
      
              for (view in childViews) {
                 auto.excludeChildren(view, excludeChildView)
              }
      
              return auto;
          }
      

      在 Java 中:TransitionHelpers.java

      我正在使用android-retrostream 来支持低于 24 的 Android API,并且仍在使用 Java 8 lambda & java.util.* -&gt; java9.util.*

      或者您可以使用Core Library Desugaring,但它会增加额外的构建时间,动态功能也存在问题here

      import java9.util.stream.Stream;
      
          public static AutoTransition useAutoTransition(View childView, boolean excludeChildView) {
              AutoTransition auto = new AutoTransition();
              auto.excludeChildren(childView, excludeChildView);
      
              return auto;
          }
      
          public static AutoTransition useAutoTransition(@IdRes int childViewId, boolean excludeChildView) {
              AutoTransition auto = new AutoTransition();
              auto.excludeChildren(childViewId, excludeChildView);
      
              return auto;
          }
      
          public static AutoTransition useAutoTransitions(final boolean excludeChildView, @IdRes Integer... childViewIds) {
              final AutoTransition auto = new AutoTransition();
      
              Stream<Integer> ids = Stream.of(childViewIds);
              ids.forEach(id -> auto.excludeChildren(id, excludeChildView));
      
              return auto;
          }
      
          public static AutoTransition useAutoTransitions(final boolean excludeChildView, View... childViews) {
              final AutoTransition auto = new AutoTransition();
      
              Stream<View> views = Stream.of(childViews);
              views.forEach(view -> auto.excludeChildren(view, excludeChildView));
      
              return auto;
          }
      

      ThatFragment.kt

      // I'm using KTX Syntethic here
      import kotlinx.android.synthetic.main.activity_transitions.layoutParent
      import kotlinx.android.synthetic.main.activity_transitions.recyclerViewChild
      
      // Let's implemented it on TransitionManager
      override protected fun onCreate(savedInstanceState: Bundle) {
        // single RecyclerView child using View class
        TransitionManager.beginDelayedTransition(layoutParent, useAutoTransition(recyclerViewChild, true))
      
        // single RecyclerView child using @IdRes
        TransitionManager.beginDelayedTransition(layoutParent, useAutoTransition(R.id.rv_1, true))
      
        // multiple RecyclerView child                         
        TransitionManager.beginDelayedTransition(layoutParent, useAutoTransitions(true, R.id.rv_child_1, R.id.rv_child_2, R.id.rv_child_n))
      }
      

      希望它很容易理解!

      感谢:

      参考资料:

      【讨论】:

        猜你喜欢
        • 2013-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-04
        • 1970-01-01
        相关资源
        最近更新 更多