"闪烁的图像"从服务器下载图像时使用ViewHolder模式

时间:2015-09-09 14:41:27

标签: java android imageview android-viewholder

我想创建一个视图列表,当您滚动列表(延迟加载)时,每个视图中显示的图像都会从服务器下载。这是我到目前为止的代码:

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显示了它的外观:

Preview

显然,我希望图像只显示在应有的位置。有没有什么好方法可以让这项工作完成?

2 个答案:

答案 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;
}