我的自定义适配器有以下代码,其中每一行都包含一个名称,以及一张从firebase存储中获取的图片:
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 com.squareup.picasso.Picasso;
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();
}
@Override
public Object getItem(int position) {
return 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) {
final ViewHolder holder;
final RowItem row_pos = rowItems.get(position);
final StorageReference storageReference = FirebaseStorage.getInstance().getReference().child(row_pos.getFirebaseUserUid()+".jpg");
//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());
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) {
Picasso.get()
.load(R.drawable.user)
.resize(70,70)
.into(holder.profile_pic);
}
}
});
return convertView;
}
}
现在,如果我像这样运行它,它将加载每一个新行,因此滚动不会像我希望的那样平滑。 为了解决这个问题,许多帖子建议添加以下内容(基本上在上面的代码中标记为注释):
if (convertView == null){
/.../
convertView.setTag(holder);
}
} else {
holder = (ViewHolder) convertView.getTag();
}
但是当我这样做时,在列表项的profilePic视图中,它会一个接一个地显示很多图像。
任何人都可以帮助检测问题并建议任何解决方案吗?
以下是该错误的视频:https://vimeo.com/271555362
答案 0 :(得分:0)
我认为问题在于Eselfar表示的问题。 当您请求uri到firebase时,它应该在异步线程上完成。这意味着同时检索迭代n的url,适配器继续在列表上迭代。
这两个流程同时执行,因此持有者变量会不断重新分配,可能直到迭代到达其最后一个可见项目(视图)。
这导致了这种情况:
for each view {
request image url (for example 2 seconds)
assign holder variable (for example 0.0002 seconds)
}
同时,第一个图像url被检索(2秒),适配器已经结束了迭代。
所以holder变量现在具有相同的值,即整个迭代的最后一个值。
在第一个图像网址停留2秒后(依次为剩余的图像网址),它进入OnSuccessListener.onSuccess
方法,然后使用Glide设置holder.profile_pic
变量上的图像。但是,如前所述,该变量现在属于最后一次迭代。
所以你的问题是你正在混合同步和异步方法。
可能的解决方案是首先检索所有图像网址,然后删除该异步任务(storageReference.getDownloadUrl()
)并在同一个UIThread中执行所有操作。
我认为最简单的解决方案(但可能不是最好的)是创建一个自定义OnSuccessListener和一个自定义OnFailureListener,如下所示:
public class MySuccessListener extends OnSuccessListener {
Holder holder;
public MySuccessListener(Holder holder) {
this.holder = holder;
super();
}
@Override
public void onSuccess(Uri uri) {
Glide.with(context).load(uri).apply(RequestOptions.circleCropTransform()).into(holder.profile_pic);
}
}
public class MyFailureListener extends OnFailureListener {
Holder holder;
public MyFailureListener(Holder holder) {
this.holder = holder;
super();
}
@Override
public void onFailure(@NonNull Exception exception)
int errorCode = ((StorageException) exception).getErrorCode();
if (errorCode == StorageException.ERROR_OBJECT_NOT_FOUND) {
Picasso.get()
.load(R.drawable.user)
.resize(70,70)
.into(holder.profile_pic);
}
}
}
请注意,您将当前持有者传递给侦听器构造函数,因此当异步任务结束时,您可以定位正确的持有者。 然后你可以这样更新你的代码:
storageReference.getDownloadUrl().addOnSuccessListener(new MyFailureListener<Uri>(holder))
.addOnFailureListener(new MyFailureListener(holder));
现在我无法测试它,IDE我的代码可能不精确,但基本上你应该理解建议的方式。
请注意,您正在使用Picasso和Glide。这两个库做同样的事情,我建议你使用其中一个并删除不必要的依赖。