我想创建一个视图列表,当您滚动列表(延迟加载)时,每个视图中显示的图像都会从服务器下载。这是我到目前为止的代码:
public class CustomAdapter extends ArrayAdapter<Component> {
private final List<Component> components;
private final Activity activity;
public CustomAdapter(Activity context, List<Component> components) {
super(context, android.R.layout.simple_list_item_1, components);
this.components = components;
this.activity = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = activity.getLayoutInflater();
convertView = inflater.inflate(R.layout.item, null, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder)convertView.getTag();
}
Component component = components.get(position);
// Don't show any image before the correct one is downloaded
viewHolder.imageView.setImageBitmap(null);
if (component.getImage() == null) { // Image not downloaded already
new DownloadImageTask(viewHolder.imageView).execute(component);
} else {
viewHolder.imageView.setImageBitmap(component.getImage());
}
return convertView;
}
private class ViewHolder {
ImageView imageView;
}
private class DownloadImageTask extends AsyncTask<Component, Void, Component> {
private ImageView imageView;
public DownloadImageTask(ImageView imageView) {
this.imageView = imageView;
}
@Override
protected Component doInBackground(Component... params) {
String url = params[0].getImageURL();
Component component = params[0];
// Download the image using the URL address inside found in component
Bitmap image = ImageDownloader.getImage(url);
// Set the Bitmap image to the component so we don't have to download it again
component.setImage(image);
return component;
}
@Override
protected void onPostExecute(Component component) {
// Update the ImageView with the downloaded image and play animation
imageView.setImageBitmap(component.getImage());
Animation animation = AnimationUtils.loadAnimation(activity, R.anim.fade_in);
imageView.startAnimation(animation);
}
}
}
基本上,当运行getView()时,它会从Component获取数据(在本例中为Bitmap)(用于缓存项目中的数据),除非没有数据。在这种情况下,它执行DownloadImageTask,它将下载图像并将其存储在Component中。存储后,它会将图像放入ImageView。
我的问题是,当使用ViewHolder模式时,使用ImageViews,而不是&#34;错误的方式&#34; (每次调用findViewById()),滚动列表会使错误的ImageViews获取下载的Bitmap。这个gif显示了它的外观:
显然,我希望图像只显示在应有的位置。有没有什么好方法可以让这项工作完成?
答案 0 :(得分:2)
我用Glide解决了这个问题。感谢大家告诉我那里已经存在这么棒的事情了!
做(差不多)同样的事情就像:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = activity.getLayoutInflater();
convertView = inflater.inflate(R.layout.item, null, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
String url = components.get(position).getImageURL();
Glide.with(activity).load(url).crossFade().into(viewHolder.imageView);
return convertView;
}
答案 1 :(得分:1)
虽然其他人是正确的,有许多库可以为您处理这个问题,但是如果您真的想要,可以按照您最初尝试的方式执行此操作。您只需要确保在项目视图被回收时取消AsyncTasks。您可以通过将AsyncTask添加到ViewHolder类来完成此操作,这样您就可以查看是否有某些内容在运行并取消它,然后再开始新的。
private class ViewHolder {
ImageView imageView;
AsyncTask<?,?,?> task;
}
然后在你的getView()中:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
// Don't show any image before the correct one is downloaded
viewHolder.imageView.setImageBitmap(null);
if (viewHolder.task != null) { // Existing task may be executing
viewHolder.task.cancel(true);
viewHolder.task = null;
}
if (component.getImage() == null) { // Image not downloaded already
AsyncTask<?,?,?> task = new DownloadImageTask(viewHolder.imageView);
viewHolder.task = task;
task.execute(component);
} else {
viewHolder.imageView.setImageBitmap(component.getImage());
}
return convertView;
}