如何在RecyclerView的onCreateViewHolder中异步返回ViewHolder?
问题描述
想要在RecyclerView的onCreateViewHolder()方法中异步返回ViewHolder。初始时第一行代码设置的viewHolder可以正常工作,但希望回调触发后,viewHolder更新为回调内设置的实例,当前代码无法实现该效果,相关代码如下:
@NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { viewHolder = new VHStart(LayoutInflater.from(fragment.requireContext()).inflate(R.layout.item_note_loading,parent,false)); if (viewType == VIEW_TYPE_ONE) { MyView myView = new MyView(parent.getContext(), this); myView.inflate(new OnFinishedInflated() { @Override public void onFinished(View view) { viewHolder = new VHSingleElement(view); } }); } else if (viewType == VIEW_TYPE_TWO) { // will add this later } else throw new RuntimeException("No such view type"); return viewHolder; }
解决方案
为什么原代码无效
onCreateViewHolder()是同步方法,必须立即返回ViewHolder实例。你在异步回调里修改viewHolder变量时,这个方法早已经把初始的VHStart返回给RecyclerView了,后续的赋值根本不会影响已经返回的实例,自然无法更新UI。
正确实现思路
不能在onCreateViewHolder()里做异步加载,换个思路实现:
- 先返回加载态ViewHolder:就是你原来的
VHStart,先展示加载布局占住位置。 - 把异步逻辑移到ViewHolder内部:在ViewHolder创建完成后,异步加载目标View,加载完直接更新当前ViewHolder的内容。
- 按需通知刷新:如果需要绑定数据,加载完成后可调用
notifyItemChanged()确保数据和UI匹配。
具体代码示例
第一步:改造ViewHolder,支持动态更新View
public class MyViewHolder extends RecyclerView.ViewHolder { private View currentView; public MyViewHolder(View itemView) { super(itemView); currentView = itemView; } // 加载完成后替换当前View public void updateContentView(View newView) { ViewGroup parent = (ViewGroup) currentView.getParent(); if (parent == null) return; // 移除旧View,插入新View到原位置 int childIndex = parent.indexOfChild(currentView); parent.removeView(currentView); parent.addView(newView, childIndex); currentView = newView; // 这里可以添加新View的数据绑定逻辑 // bindDataToNewView(newView); } }
第二步:修改onCreateViewHolder()逻辑
@NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_ONE) { // 先返回加载态ViewHolder View loadingView = LayoutInflater.from(fragment.requireContext()) .inflate(R.layout.item_note_loading, parent, false); MyViewHolder holder = new MyViewHolder(loadingView); // 异步加载目标View,完成后更新当前ViewHolder MyView myView = new MyView(parent.getContext(), this); myView.inflate(new OnFinishedInflated() { @Override public void onFinished(View view) { holder.updateContentView(view); // 如果需要刷新数据,可调用notifyItemChanged(getAdapterPosition()) } }); return holder; } else if (viewType == VIEW_TYPE_TWO) { // 后续实现其他类型的ViewHolder return null; } else { throw new RuntimeException("No such view type"); } }
备选方案:用ViewStub简化加载流程
如果MyView的布局可以提前定义,也可以用ViewStub实现延迟加载:
- 在加载布局
item_note_loading.xml中添加ViewStub:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical"> <!-- 加载中UI --> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content"/> <!-- 目标布局的Stub --> <ViewStub android:id="@+id/target_view_stub" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout="@layout/item_single_element"/> </LinearLayout>
- 修改ViewHolder处理逻辑:
public class MyViewHolder extends RecyclerView.ViewHolder { private ViewStub targetStub; public MyViewHolder(View itemView) { super(itemView); targetStub = itemView.findViewById(R.id.target_view_stub); } public void inflateTargetView() { View targetView = targetStub.inflate(); // 绑定数据到targetView } }
- 在
onCreateViewHolder()中调用:
@NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_ONE) { View loadingView = LayoutInflater.from(fragment.requireContext()) .inflate(R.layout.item_note_loading, parent, false); MyViewHolder holder = new MyViewHolder(loadingView); MyView myView = new MyView(parent.getContext(), this); myView.inflate(new OnFinishedInflated() { @Override public void onFinished(View view) { holder.inflateTargetView(); } }); return holder; } // ...其他类型处理 }
注意事项
- 异步加载要处理异常,比如加载失败时显示错误UI。
- 如果列表项有数据,确保View更新后重新绑定数据,避免数据和UI不匹配。
- 可以给ViewHolder加个标记(比如
isLoaded),防止重复触发加载。
内容的提问来源于stack exchange,提问作者user21172734
相关产品推荐
相关产品推荐

