基于C#高效实现多ViewHolder与多View及Fragment场景适配问询
嘿,这个场景我之前做类似分类列表需求的时候碰到过,核心就是把「数据源过滤」和「多类型ViewHolder复用」拆解开处理,既高效又好维护。下面给你一套具体的实现思路:
核心实现方案
1. 统一数据模型,标记Item类型
先给所有对象定义一个基接口,让A、B、C、D都实现它,这样既能用统一的集合存储,又能快速区分每个Item的类型:
// 定义基接口,标记Item类型 public interface BaseItem { ItemType GetItemType(); } // 枚举所有Item类型,方便后续判断 public enum ItemType { TypeA, TypeB, TypeC, TypeD } // A类型实体类示例 public class A : BaseItem { // A的业务属性 public string UniqueAProperty { get; set; } public ItemType GetItemType() => ItemType.TypeA; } // B、C、D类同理,各自实现GetItemType返回对应的枚举值
2. 针对不同Fragment做数据源过滤
不需要给每个Fragment单独存数据副本,基于主集合做按需过滤即可。如果数据量大或切换频繁,可以缓存过滤结果提升性能:
基础过滤方式(适合小数据量场景)
假设主集合在ViewModel或全局单例中维护,每个Fragment直接通过Linq过滤:
// 主集合(示例:ViewModel中维护) private List<BaseItem> _allItems = new List<BaseItem>(); // Fragment 2 只显示A类型 public List<BaseItem> GetFragment2Items() { return _allItems.Where(item => item.GetItemType() == ItemType.TypeA).ToList(); } // Fragment 4 显示C和D类型 public List<BaseItem> GetFragment4Items() { return _allItems.Where(item => item.GetItemType() == ItemType.TypeC || item.GetItemType() == ItemType.TypeD).ToList(); }
缓存优化(适合大数据量/频繁切换场景)
在ViewModel中维护每个Fragment对应的过滤后列表,主数据更新时同步更新缓存:
// 缓存每个Fragment的过滤结果 private Dictionary<FragmentType, List<BaseItem>> _filteredCache = new Dictionary<FragmentType, List<BaseItem>>(); // 主数据更新时,同步更新所有缓存 public void UpdateAllItems(List<BaseItem> newItems) { _allItems = newItems; // 更新Fragment2的缓存 _filteredCache[FragmentType.Fragment2] = _allItems.Where(item => item.GetItemType() == ItemType.TypeA).ToList(); // 更新Fragment4的缓存,以此类推... } // Fragment直接取缓存数据 public List<BaseItem> GetCachedItems(FragmentType type) { return _filteredCache.TryGetValue(type, out var items) ? items : new List<BaseItem>(); }
3. 实现多类型ViewHolder的RecyclerView Adapter
这是核心部分,通过重写GetItemViewType让RecyclerView识别不同Item类型,然后创建对应的ViewHolder:
第一步:创建各类型的ViewHolder
把每个Item的布局绑定逻辑封装在对应的ViewHolder里,让Adapter更简洁:
// A类型的ViewHolder public class AViewHolder : RecyclerView.ViewHolder { private TextView _aPropertyTv; public AViewHolder(View itemView) : base(itemView) { // 初始化控件 _aPropertyTv = itemView.FindViewById<TextView>(Resource.Id.tv_a_property); } // 绑定数据的方法 public void Bind(A item) { _aPropertyTv.Text = item.UniqueAProperty; // 其他绑定逻辑,比如设置点击事件等 } } // B、C、D类型的ViewHolder同理,各自绑定对应的布局和数据
第二步:实现多类型Adapter
重写GetItemViewType、OnCreateViewHolder和OnBindViewHolder方法:
public class MultiTypeItemAdapter : RecyclerView.Adapter { private List<BaseItem> _items; public MultiTypeItemAdapter(List<BaseItem> items) { _items = items; } // 重写获取Item类型的方法,返回对应的枚举值转int public override int GetItemViewType(int position) { return (int)_items[position].GetItemType(); } // 根据ViewType创建对应的ViewHolder public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { var inflater = LayoutInflater.From(parent.Context); switch ((ItemType)viewType) { case ItemType.TypeA: var aView = inflater.Inflate(Resource.Layout.item_type_a, parent, false); return new AViewHolder(aView); case ItemType.TypeB: var bView = inflater.Inflate(Resource.Layout.item_type_b, parent, false); return new BViewHolder(bView); case ItemType.TypeC: var cView = inflater.Inflate(Resource.Layout.item_type_c, parent, false); return new CViewHolder(cView); case ItemType.TypeD: var dView = inflater.Inflate(Resource.Layout.item_type_d, parent, false); return new DViewHolder(dView); default: throw new ArgumentOutOfRangeException(nameof(viewType), "未知的Item类型"); } } // 根据ViewHolder类型绑定对应的数据 public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { var item = _items[position]; switch (holder) { case AViewHolder aHolder: aHolder.Bind((A)item); break; case BViewHolder bHolder: bHolder.Bind((B)item); break; case CViewHolder cHolder: cHolder.Bind((C)item); break; case DViewHolder dHolder: dHolder.Bind((D)item); break; } } public override int ItemCount => _items?.Count ?? 0; // 提供更新数据源的方法,方便Fragment数据变化时刷新 public void UpdateItems(List<BaseItem> newItems) { _items = newItems; NotifyDataSetChanged(); // 如果需要更精细的刷新,可以用NotifyItemRangeInserted等方法,性能更好 } }
4. Fragment中使用Adapter
每个Fragment初始化自己的Adapter,传入过滤后的数据源即可:
public class Fragment1 : Fragment { private MultiTypeItemAdapter _adapter; private RecyclerView _recyclerView; public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { var view = inflater.Inflate(Resource.Layout.fragment_recycler, container, false); _recyclerView = view.FindViewById<RecyclerView>(Resource.Id.recycler_view); // 初始化LayoutManager _recyclerView.SetLayoutManager(new LinearLayoutManager(Context)); // 获取过滤后的数据源(比如从ViewModel获取) var items = ViewModel.GetFragment1Items(); // Fragment1显示所有类型 _adapter = new MultiTypeItemAdapter(items); _recyclerView.SetAdapter(_adapter); return view; } // 当主数据更新时,刷新Adapter public void OnDataUpdated() { var newItems = ViewModel.GetFragment1Items(); _adapter.UpdateItems(newItems); } }
5. 额外优化建议
- 复用Adapter实例:每个Fragment保留自己的Adapter对象,不要每次切换都重新创建,只更新数据源即可,减少对象创建开销。
- 精细化刷新:如果主数据只是局部更新,不要直接调用
NotifyDataSetChanged(),而是用NotifyItemInserted、NotifyItemChanged等方法,提升RecyclerView的刷新性能。 - 布局复用:如果C和D类型的布局很相似,可以考虑复用同一个布局和ViewHolder,通过数据类型来区分显示逻辑,减少ViewHolder的数量。
内容的提问来源于stack exchange,提问作者iLikeKFC
相关产品推荐
相关产品推荐

