【问题标题】:Add fragment in RecyclerView.ViewHolder在 RecyclerView.ViewHolder 中添加片段
【发布时间】:2017-07-01 13:02:06
【问题描述】:

我有一个 RecyclerView.ViewHolder,它将根据传递的对象的实例将不同的片段添加到其 FrameLayout 中。问题在于几乎不可能将片段添加到 ViewHolder 中。请注意,我已经从父级传递了 FragmentManager。最初我尝试使用此代码

public void setSomething(boolean A) {
    if (A) {
         mFragmentManager.beginTransaction()
            .replace(mBinding.typeContainerLayout.getId(), new FragmentA())
            .commit();
    } else {
        mFragmentManager.beginTransaction()
            .replace(mBinding.typeContainerLayout.getId(), new FragmentB())
            .commit();
    }
}

这段代码的问题是所有的 ViewHolder 共享相同的 id,因此只有一个 ViewHolder 可以添加片段。在我的 RecyclerView 中,只有第一个单元格添加了片段。为了解决这个问题,我创建了另一个 FrameLayout 并将其添加到 typeContainerLayout。现在我的代码变成了这样。

public void setSomething(boolean A) {
    FrameLayout frameLayout = new FrameLayout(mContext);
    frameLayout.setId(View.generateViewId());
    mBinding.typeContainerLayout.removeAllViews();
    mBinding.typeContainerLayout.addView(frameLayout)

    if (A) {
         mFragmentManager.beginTransaction()
            .replace(frameLayout.getId(), new FragmentA())
            .commit();
    } else {
        mFragmentManager.beginTransaction()
            .replace(frameLayout.getId(), new FragmentB())
            .commit();
    }
}

现在每个 ViewHolder 都正确添加了片段并拥有自己的片段。但是,当我添加了 5 个 ViewHolder 并尝试向下滚动 RecyclerView 时,出现了运行时错误,出现了哪个状态

java.lang.IllegalArgumentException: No view found for id 0x4 (unknown) for fragment FragmentA{7c55a69 #0 id=0x4 FragmentA}
                      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1292)
                      at android.support.v4.app.FragmentManagerImpl.moveFragmentsToInvisible(FragmentManager.java:2323)
                      at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2136)
                      at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2092)
                      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1998)
                      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:709)
                      at android.os.Handler.handleCallback(Handler.java:739)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:148)
                      at android.app.ActivityThread.main(ActivityThread.java:5417)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

我的猜测是 id 在某些时候发生冲突,或者视图由于 ViewHolder 模式而被破坏。所以我的问题是。

1) 有什么解决方法吗?

2)有没有比添加片段更好的做法。我添加片段的原因是 ViewHolder 的子项的逻辑都可以位于单个片段中。当然,我可以将片段的两个视图都放入 ViewHolder xml。并且只是 setVisible() 取决于条件。但这只会让我的 ViewHolder 包含太多逻辑。

如果有人对我为什么需要片段感到困惑。这就是我想要达到的目标。 The image

【问题讨论】:

  • 你不要在 recyclerview 中使用片段。您可能需要使用ViewPager
  • 为了解决这个问题,您可以在视图持有者中添加一个 FrameLayout 以获得一个 Id。通常,我们通过扩展 RecyclerView.Adapter 来使用适配器
  • 我有一个项目清单。根据对象的实例,项目的 ViewHolder 的底部是不同的。这就是我使用片段的原因。它们都共享相同的顶部。
  • 不要使用FragmentManager:在项目布局xml中添加你的片段
  • 我遇到了同样的问题。我解决了它。只需为您的容器布局设置新的唯一 ID,您就可以将任何片段添加到您的 recyclerview 项目。这个答案对我有帮助。 stackoverflow.com/a/42994810/1931613。例如,myContainerLayout.setId(SystemClock.currentThreadTimeMillis().toInt())

标签: android android-fragments android-viewholder


【解决方案1】:

您应该确切地知道recyclerview 创建了您的持有人并为他绘制了一个视图,因此您需要在适配器的onViewAttachedToWindowmethod 中附加一个片段。在您的“附加片段”方法中,您应该检查片段管理器是否已经包含这些片段以防止创建多个实例。

适配器:

 override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {    
    if(holder is FragmentsHolder){
        attachFragmentToContainer(holder.flContainer)            
    }
    super.onViewAttachedToWindow(holder)
}

方法实现:

 fun attachFragmentToContainer(containerId: Int) {
    val fragment = if (fragmentManager?.fragments?.firstOrNull { it is YourFragment } == null)
        YourFragment.instance()
    else
        null

    if (fragment != null) {
        fragmentManager?.beginTransaction()
            ?.add(containerId, fragment)
            ?.commitNowAllowingStateLoss()
    }
}

这在大量用户上进行了测试 - 没有崩溃,性能良好。

【讨论】:

  • 这个解决方案比在onViewAttachedToWindow 中调用fragmentManager.beginTransaction().replace(holder.flContainer.id, fragment).commitNowAllowingStateLoss() 更好吗?
【解决方案2】:

简短的回答:您不应该在 recyclerView 中使用片段,这不是它们的用途。

长答案:here

【讨论】:

  • 也许你是对的。使用片段是一个糟糕的设计。感谢您的回答
  • 很高兴为您提供帮助,如果有帮助,您可以标记我的答案:)
  • 我不认为这是一个正确的答案,当然我们可以在recyclerView中使用fragments。如果您的应用程序中有一个复杂的 UI,有时需要使用某种 hack。请在下面查看我的答案:stackoverflow.com/a/56324168/5533118
  • 这个答案不正确。 androidx ViewPager2 在 RecyclerView 中使用 Fragments。见Adapter的placeFragmentInViewHolder方法:github.com/androidx/androidx/blob/androidx-master-dev/…
【解决方案3】:

我遇到了同样的问题,并通过在我的 onBindViewHolder 回调中使用 addOnChildAttachStateChangeListener RecyclerView 回调解决了它:

listView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
        @Override
        public void onChildViewAttachedToWindow(@NotNull View view) {
            //Create your fragment container here using the view param and add your fragment to the container
        }

        @Override
        public void onChildViewDetachedFromWindow(@NotNull View view) {
            //Destroy the fragment container here

        }
    });

【讨论】:

    【解决方案4】:

    onBindViewHolder:

    yourFragment fragment = new yourFragment();
        try {
            viewHolder.Fragment(fragment);
        }catch (Exception e){
            Toast.makeText(context, "BindViewHolder"+e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    

    ViewHolder:

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView name;
        FrameLayout container;
    
        private ViewHolder(@NonNull View itemView) {
            super(itemView);
    
            container = itemView.findViewById(R.id.fragmentContainer);
        }
    
        private void Fragment(final Fragment fragment) {
            FragmentTransaction transaction = ((AppCompatActivity) context).getSupportFragmentManager()
                    .beginTransaction();
            try {
                transaction.add(R.id.fragmentContainer, fragment)
                        .commit();
            } catch (Exception e) {
                Toast.makeText(context, "ViewHolder " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-18
      • 2021-11-08
      • 2016-09-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多