RecyclerView列表项中FragmentTransaction仅作用于首个元素问题
问题:RecyclerView列表项中FragmentContainerView加载Fragment异常
在RecyclerView适配器的onCreateViewHolder()方法中加载包含FragmentContainerView的布局后,尝试为每个列表项的容器添加不同Fragment时,出现所有Fragment内容仅显示在首个列表项的容器中的异常。
相关代码
列表项布局:listitem_layout.xml
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#D47F00"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
适配器onCreateViewHolder方法
@NonNull @Override public CustomHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = inflater.inflate(R.layout.listitem_layout, parent, false); return new CustomHolder(itemView, (AppCompatActivity) inflater.getContext()); }
CustomHolder实现
class CustomHolder extends RecyclerView.ViewHolder { public static boolean skip = true; private AppCompatActivity appCompatActivity; private FragmentContainerView fragmentContainerView; private Fragment attributeDetailFragment; private Trainer trainer; private TextView trainerAge; CustomHolder(@NonNull View itemView, AppCompatActivity appCompatActivity) { super(itemView); this.trainerAge = itemView.findViewById(R.id.trainer_age); this.fragmentContainerView = itemView.findViewById(R.id.fragment_container); this.appCompatActivity = appCompatActivity; } void setTrainer(Trainer trainer) { this.trainer = trainer; this.trainerAge.setText(" " + trainer.getAge()); this.trainerAge.setOnClickListener(this::onTrainerAgeClick); itemView.setTag(this); } private void onTrainerAgeClick(View view) { attributeDetailFragment = new AgeFragment(); showFragment(); } private void showFragment() { Bundle args = new Bundle(); args.putParcelable("trainer", trainer); attributeDetailFragment.setArguments(args); FragmentManager fragmentManager = appCompatActivity.getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.replace(fragmentContainerView.getId(), attributeDetailFragment); fragmentTransaction.commit(); } }
问题原因
所有列表项的FragmentContainerView使用了同一个固定id(R.id.fragment_container),而FragmentManager是通过id来定位容器的。调用replace()时,FragmentManager会找到布局中第一个匹配该id的容器,也就是首个列表项的FragmentContainerView,因此所有Fragment都会被替换到这个容器中。
解决方案
方案1:为每个容器设置唯一id
在onCreateViewHolder中,为每个加载的FragmentContainerView生成唯一id,避免id冲突:
@NonNull @Override public CustomHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = inflater.inflate(R.layout.listitem_layout, parent, false); FragmentContainerView container = itemView.findViewById(R.id.fragment_container); // 生成唯一id container.setId(View.generateViewId()); return new CustomHolder(itemView, (AppCompatActivity) inflater.getContext()); }
方案2:直接传入容器实例(AndroidX Fragment 1.2.0+)
从AndroidX Fragment 1.2.0版本开始,FragmentTransaction支持直接传入FragmentContainerView实例,无需依赖id定位:
private void showFragment() { Bundle args = new Bundle(); args.putParcelable("trainer", trainer); attributeDetailFragment.setArguments(args); FragmentManager fragmentManager = appCompatActivity.getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); // 直接传入容器实例 fragmentTransaction.replace(fragmentContainerView, attributeDetailFragment); fragmentTransaction.commit(); }
额外注意事项
- RecyclerView存在列表项复用机制,需在
onBindViewHolder中处理Fragment的状态恢复或清理,避免复用导致的显示异常。 - 若列表项数量较多,频繁创建Fragment可能影响性能,可考虑用动态切换View的方式替代Fragment,减少实例创建开销。
内容的提问来源于stack exchange,提问作者AdrianKenobi
相关产品推荐
相关产品推荐

