Android自定义Adapter复用View时图片频繁切换问题求助
解决ListView适配器复用后图片错乱+滚动卡顿问题
问题拆解
你现在碰到两个头疼的问题:没启用ViewHolder时列表滚动卡顿,启用后又出现头像快速切换错乱的情况。咱们一步步来搞定它们:
问题根源分析
- 滚动卡顿原因:没启用ViewHolder时,每次
getView都会重新解析布局、查找控件,这是非常消耗资源的操作,自然导致滚动不流畅。 - 图片错乱原因:核心是异步加载和View复用的冲突。滚动列表时,旧的列表项View会被回收给新的位置,但之前发起的Firebase URL请求、Glide图片加载都是异步操作,旧请求完成后会把错误的图片加载到已经复用的View上,就出现了你看到的图片跳来跳去的情况。
修复方案
下面是优化后的完整代码,关键改动我都标出来了:
import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.firebase.storage.FirebaseStorage; import com.google.firebase.storage.StorageException; import com.google.firebase.storage.StorageReference; import java.util.List; public class CustomAdapter extends BaseAdapter { Context context; List<RowItem> rowItems; CustomAdapter(Context context, List<RowItem> rowItems) { this.context = context; this.rowItems = rowItems; } @Override public int getCount() { return rowItems.size(); } // 优化:原来返回position,现在直接返回对应RowItem,逻辑更合理 @Override public RowItem getItem(int position) { return rowItems.get(position); } @Override public long getItemId(int position) { return position; } /* private view holder class */ private class ViewHolder { ImageView profile_pic; TextView member_name; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; final RowItem row_pos = getItem(position); // 1. 正确启用ViewHolder复用(恢复你注释的核心代码) if (convertView == null) { LayoutInflater mInflater = LayoutInflater.from(context); convertView = mInflater.inflate(R.layout.list_item, parent, false); holder = new ViewHolder(); holder.member_name = convertView.findViewById(R.id.member_name); holder.profile_pic = convertView.findViewById(R.id.profile_pic); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 设置名称 holder.member_name.setText(row_pos.getName()); // 2. 关键修复:加载新图片前清除旧请求,避免旧请求覆盖新内容 Glide.with(context).clear(holder.profile_pic); // 3. 设置默认占位图,加载过程中显示,减少视觉错乱 holder.profile_pic.setImageResource(R.drawable.user); // 获取Firebase Storage引用 StorageReference storageReference = FirebaseStorage.getInstance().getReference() .child(row_pos.getFirebaseUserUid() + ".jpg"); storageReference.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() { @Override public void onSuccess(Uri uri) { // 加载网络图片 Glide.with(context) .load(uri) .apply(RequestOptions.circleCropTransform()) .into(holder.profile_pic); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { int errorCode = ((StorageException) exception).getErrorCode(); if (errorCode == StorageException.ERROR_OBJECT_NOT_FOUND) { // 图片不存在时显示默认图 holder.profile_pic.setImageResource(R.drawable.user); } } }); return convertView; } }
额外性能优化建议
- 缓存Firebase下载URL:每次调用
getDownloadUrl()都会发起网络请求,你可以把获取到的URL存在RowItem对象里,下次直接使用,避免重复请求。比如给RowItem添加private String avatarUrl;字段,成功获取URL后赋值,后续直接用这个URL加载图片。 - 迁移到RecyclerView:ListView属于较旧的组件,RecyclerView的ViewHolder机制更完善,性能更好,还支持更多布局类型,是当前Android列表组件的首选,建议后续考虑迁移。
内容的提问来源于stack exchange,提问作者ITG
相关产品推荐
相关产品推荐

