在ListView适配器中使用异步任务加载位图

时间:2015-07-06 21:59:00

标签: android android-listview android-adapter

我在AsyncTask类中尝试管理位图时遇到了麻烦。

我认为我已经设置好了但是当我尝试显示代码在循环中执行的图像时会发生什么,并且相同的图像视图最终会出现故障,同时在同一图像中设置大量不同的图像(&& #39;很难解释)。我正在从我的适配器中的getView方法执行AsyncTask。

这是我的ListView适配器类,也许有人可以弄清楚它有什么问题:

    public class SongsAdapterNew extends ArrayAdapter<String> {

    ArrayList<String> names;
    ArrayList<String> images;
    Activity context;
    ImageView imageView;

    static class ViewHolder {

        private TextView text;
        private ImageView image;
    }

    public SongsAdapterNew(Activity context, ArrayList<String> names,
            ArrayList<String> images) {
        super(context, R.layout.songs_row, names);

        this.names = names;
        this.context = context;
        this.images = images;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub

        ViewHolder mViewHolder = null;

        if (convertView == null) {
            mViewHolder = new ViewHolder();
            LayoutInflater vi = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = vi.inflate(R.layout.songs_row, parent, false);
            mViewHolder.text = (TextView) convertView
                    .findViewById(R.id.songTextView);
            imageView = mViewHolder.image = (ImageView) convertView
                    .findViewById(R.id.imageView1);
            convertView.setTag(mViewHolder);

        }

        else {

            mViewHolder = (ViewHolder) convertView.getTag();

        }

        mViewHolder.text.setText(names.get(position));

        new ImageLoader().execute(mViewHolder.image, images.get(position));

        return convertView;
    }

    public class ImageLoader extends AsyncTask<Object, String, Bitmap> {

        private View view;
        private Bitmap bitmap = null;
        MediaMetadataRetriever mmr;
        byte[] rawArt = null;
        Bitmap art1;
        BitmapFactory.Options bfo;

        @Override
        protected Bitmap doInBackground(Object... parameters) {

            // Get the passed arguments here
            view = (View) parameters[0];
            String uri = (String) parameters[1];

            mmr = new MediaMetadataRetriever();

            bfo = new BitmapFactory.Options();
            bfo.inSampleSize = 5;
            try {
                mmr.setDataSource(uri);
                rawArt = mmr.getEmbeddedPicture();
                bitmap = BitmapFactory.decodeByteArray(rawArt, 0,
                        rawArt.length, bfo);
            } catch (Exception e) {
            }


            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null) {

                imageView.setImageBitmap(bitmap);
            }

            else {

                imageView.setImageResource(R.drawable.songs);
            }

        }
    }

}

3 个答案:

答案 0 :(得分:0)

我使用了类似于你的东西,避开像Picasso或Universal UIL这样的大型图书馆,总有一天我可能会提高性能。但是使用AsyncTask有点棘手,但谷歌已经很好地记录了它。

无论如何,我认为你的问题是。

更改FROM:

onDestroy()

TO:

imageView.setImageBitmap(bitmap);

注意:您不应使用更​​广泛的view.setImageBitmap(bitmap); 类范围。相反,您需要在行项目中引用imageView对象访问特定图像

我想这可以解释你的说法“一次在同一张图片中设置很多不同的图像......”。

答案 1 :(得分:0)

如果您真的想远离第三方库,则需要在模型中保留Bitmap数据。由于视图被回收,因此视图(和持有者)将被交换。

这是一个粗略的决定,不是利用位图缓存,只是简单的问题。当Bitmap准备就绪时,它不会通知视图。

简单的方法是创建一个包含url和位图的Image类

public final class Image {
    public final String url;
    private Bitmap bitmap;
    private WeakReference<AsyncTask> imageLoaderWeakReference;

    public Image(String url) {
        this.url = url != null ? url : url;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public void load() {
        imageLoader = new ImageLoader() {
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                ...
                this.bitmap = bitmap;
            }
        };
        imageLoader.execute(mViewHolder.image, url);
        imageLoaderWeakReference = new WeakReference<>(imageLoader);
    }
}

然后在适配器构造函数中传递Image个实例的列表,而不是字符串 - images

最后在getView()

更新行new ImageLoader().execute(mViewHolder.image, images.get(position));
Image image = images.get(position);
Bitmap bitmap = image.getBitmap();
if (bitmap != null) {
    // set your image view with the bitmap
}
else {
    image.load();
}

最后,您可以在ImageView中添加Image弱引用,但这需要提前考虑。

答案 2 :(得分:0)

正如@TheOriginalAndroid所说,你看到的bug的来源可能是在imageView变量上设置位图(在整个适配器中有范围)而不是在视图上(在AsyncTask的范围内是btw并保持通过引用以更正列表中的ImageView

但是在AsyncTask中,由于某些原因,您需要WeakReference到该图片视图。首先,它不会使Bitmap无法进行回收,其次会因ImageView因某些原因而失败而导致崩溃,但AsyncTask onPostExecute尝试设置位图它(不再存在因此 null ImageView)。

检查this blog帖子或this video,大家解释一下并在那里展示它非常好;)