如何在RecyclerView中展示现有Fragment列表 无需重写ViewHolder逻辑
解决方案
针对你的需求,有两种成熟的实现方案,优先推荐第一种,性能更优且复用性更强:
方案1:重构Fragment为自定义View(最优解)
Fragment本身的设计场景是页面级的UI容器,和RecyclerView的Item回收机制兼容性较差,频繁创建销毁Fragment会带来不必要的性能开销。你现有ReviewFragment中的逻辑全是UI组件相关的业务,完全可以抽为自定义View,不需要重写任何原有逻辑:
- 新建自定义View类继承你布局根节点对应的布局类(比如ConstraintLayout)
- 把原Fragment中所有成员变量、
initComponent、setDetail、按钮点击事件、Presenter初始化逻辑全部复制到自定义View中,仅需将原来onCreateView的inflate逻辑迁移到自定义View的构造方法中即可 - RecyclerView的Item布局直接使用该自定义View,ViewHolder仅需持有该自定义View实例,绑定数据时直接调用公开方法即可
示例代码
自定义View实现:
public class ReviewItemView extends ConstraintLayout implements ReviewViewContract.View { private TextView tvContent, tvName,tvDate,tvReactionCounter, tvCommentCountCounter; private ImageView userPhoto; private ImageButton btnMore; private Button btnComment, btnLike; private PopupMenu popup; private ReviewViewContract.Presenter presenter; public ReviewItemView(Context context) { super(context); init(context); } public ReviewItemView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public ReviewItemView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { // 原有Fragment的inflate逻辑,直接挂载到当前自定义View inflate(context, R.layout.component_review, this); initComponent(this); } // 原有Fragment的方法直接全部复制即可,无需修改逻辑 @Override public void initComponent(View view) { userPhoto = view.findViewById(R.id.img_userphoto_profile); tvName = view.findViewById(R.id.tv_name_profile); tvDate = view.findViewById(R.id.tvdate_profile); tvContent = view.findViewById(R.id.content_review_component); tvReactionCounter = view.findViewById(R.id.tv_reactionCounter); tvCommentCountCounter = view.findViewById(R.id.tv_commentsCounter); btnComment = view.findViewById(R.id.btn_comment); btnLike=view.findViewById(R.id.btn_like); btnMore = view.findViewById(R.id.more_review); popup = new PopupMenu(view.getContext(), btnMore); popup.getMenuInflater().inflate(R.menu.menu_review, popup.getMenu()); btnMore.setOnClickListener(v->popup.show()); } @Override public void setDetail(String title, String date, String authorImageURL, String overView) { tvName.setText("ciao"); } // 其他点击事件、业务方法直接复制原有Fragment的实现 public void setData(ReviewData data) { // 对外暴露的数据传入方法,替代原有Fragment的Arguments传参 presenter = new ReviewViewPresenter(this, getContext(), data); presenter.getDetail(); } }
RecyclerView Adapter实现:
public class ReviewAdapter extends RecyclerView.Adapter<ReviewAdapter.ReviewViewHolder> { private List<ReviewData> dataList; public ReviewAdapter(List<ReviewData> dataList) { this.dataList = dataList; } @NonNull @Override public ReviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // Item直接使用自定义View ReviewItemView itemView = new ReviewItemView(parent.getContext()); return new ReviewViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull ReviewViewHolder holder, int position) { holder.reviewItemView.setData(dataList.get(position)); } @Override public int getItemCount() { return dataList.size(); } static class ReviewViewHolder extends RecyclerView.ViewHolder { ReviewItemView reviewItemView; public ReviewViewHolder(@NonNull View itemView) { super(itemView); reviewItemView = (ReviewItemView) itemView; } } }
方案2:基于ViewPager2实现(无需修改原有Fragment代码)
如果你暂时不想重构现有Fragment代码,可以用官方提供的ViewPager2实现,它底层基于RecyclerView,原生支持垂直滚动列表,且内置FragmentStateAdapter自动处理Fragment的回收、复用和事务提交,性能远优于手动add Fragment的方案:
- 布局中添加ViewPager2,设置orientation为垂直方向
- 自定义Adapter继承
FragmentStateAdapter,对应位置返回对应的ReviewView实例即可
示例代码
Activity中配置:
ViewPager2 reviewList = findViewById(R.id.review_list); // 设置为垂直滚动,和普通RecyclerView效果完全一致 reviewList.setOrientation(ViewPager2.ORIENTATION_VERTICAL); ReviewFragmentAdapter adapter = new ReviewFragmentAdapter(this, reviewDataList); reviewList.setAdapter(adapter);
Adapter实现:
public class ReviewFragmentAdapter extends FragmentStateAdapter { private List<ReviewData> dataList; public ReviewFragmentAdapter(@NonNull FragmentActivity activity, List<ReviewData> dataList) { super(activity); this.dataList = dataList; } @NonNull @Override public Fragment createFragment(int position) { ReviewView fragment = new ReviewView(); Bundle args = new Bundle(); // 和原有传参逻辑保持一致,传入对应位置的数据 args.putSerializable("review_data", dataList.get(position)); fragment.setArguments(args); return fragment; } @Override public int getItemCount() { return dataList.size(); } }
内容的提问来源于stack exchange,提问作者Ansol
相关产品推荐
相关产品推荐

