Android 中包含图像的大型 ListView [英] Large ListView containing images in Android

查看:16
本文介绍了Android 中包含图像的大型 ListView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于各种 Android 应用程序,我需要大型 ListView,即具有 100-300 个条目的视图.

For various Android applications, I need large ListViews, i.e. such views with 100-300 entries.

所有条目必须在应用程序启动时批量加载,因为需要进行一些排序和处理,否则应用程序无法知道首先显示哪些项目.

All entries must be loaded in bulk when the application is started, as some sorting and processing is necessary and the application cannot know which items to display first, otherwise.

到目前为止,我还批量加载了所有项目的图像,然后将这些图像与每个条目的其余数据一起保存在 ArrayList 中.

So far, I've been loading the images for all items in bulk as well, which are then saved in an ArrayList<CustomType> together with the rest of the data for each entry.

当然,这不是一个好的做法,因为您很可能会遇到 OutOfMemoryException 那么:对 ArrayList 中所有图像的引用阻止了垃圾收集器开始工作.

But of course, this is not a good practice, as you're very likely to have an OutOfMemoryException then: The references to all images in the ArrayList prevent the garbage collector from working.

显然,最好的解决方案是只批量加载文本数据,然后根据需要加载图像,对吗?例如,Google Play 应用程序执行此操作:您可以看到在滚动到图像时加载了图像,即它们可能是在适配器的 getView() 方法中加载的.但是对于 Google Play,这是一个不同的问题,无论如何,因为图像必须从 Internet 加载,而我的情况并非如此.我的问题不是加载图像需要太长时间,而是存储它们需要太多内存.

So the best solution is, obviously, to load only the text data in bulk whereas the images are then loaded as needed, right? The Google Play application does this, for example: You can see that images are loaded as you scroll to them, i.e. they are probably loaded in the adapter's getView() method. But with Google Play, this is a different problem, anyway, as the images must be loaded from the Internet, which is not the case for me. My problem is not that loading the images takes too long, but storing them requires too much memory.

那么我应该如何处理这些图像?在真正需要它们时加载 getView()?会使滚动缓慢.那么调用 AsyncTask 呢?或者只是一个普通的Thread?参数化它?

So what should I do with the images? Load in getView(), when they are really needed? Would make scrolling sluggish. So calling an AsyncTask then? Or just a normal Thread? Parametrize it?

我可以将已经加载的图像保存到HashMap中,这样它们就不需要在getView()中再次加载.但是如果这样做了,您又会遇到内存问题:HashMap 存储对所有图像的引用,所以最后,您可能会再次遇到 OutOfMemoryException.

I could save the images that are already loaded into a HashMap<String,Bitmap>, so that they don't need to be loaded again in getView(). But if this is done, you have the memory problem again: The HashMap stores references to all images, so in the end, you could have the OutOfMemoryException again.

我知道这里已经有很多问题讨论了图像的延迟加载".但主要是解决加载慢的问题,不会消耗太多内存.

I know that there are already lots of questions here that discuss "Lazy loading" of images. But they mainly cover the problem of slow loading, not too much memory consumption.

我现在决定在 getView() 中启动 AsyncTasks,它将图像加载到后台的 ListView 中.但这会导致我的应用程序遇到 RejectedExecutionException.我现在该怎么办?

I've now decided to start AsyncTasks in getView() which load the image into the ListView in the background. But this causes my application to run into an RejectedExecutionException. What should I do now?

推荐答案

我采用了使用 AsyncTask 加载图像并将任务附加到适配器的 getView 函数中的视图的方法,以跟踪正在加载哪个任务看法.我在我的一个应用程序中使用它,没有滚动延迟,所有图像都加载到正确的位置,没有抛出异常.另外,因为如果任务被取消,它就不起作用,你可以在你的列表上执行一个 fling,它应该完全滞后.

I took the approach of loading the images with an AsyncTask and attaching the task to the view in the adapter's getView function to keep track of which task is loading in which view. I use this in an app of mine and there's no scroll lag and all images are loaded in the proper position with no exceptions being thrown. Also, because the task does no work if it's canceled, you can perform a fling on your list and it should lag up at all.

任务:

public class DecodeTask extends AsyncTask<String, Void, Bitmap> {

private static int MaxTextureSize = 2048; /* True for most devices. */

public ImageView v;

public DecodeTask(ImageView iv) {
    v = iv;
}

protected Bitmap doInBackground(String... params) {
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inPurgeable = true;
    opt.inPreferQualityOverSpeed = false;
    opt.inSampleSize = 0;

    Bitmap bitmap = null;
    if(isCancelled()) {
        return bitmap;
    }

    opt.inJustDecodeBounds = true;
    do {
        opt.inSampleSize++;
        BitmapFactory.decodeFile(params[0], opt);
    } while(opt.outHeight > MaxTextureSize || opt.outWidth > MaxTextureSize)
    opt.inJustDecodeBounds = false;

    bitmap = BitmapFactory.decodeFile(params[0], opt);
    return bitmap;
}

@Override
protected void onPostExecute(Bitmap result) {
    if(v != null) {
        v.setImageBitmap(result);
    }
}

}

适配器存储一个 ArrayList,其中包含所有需要加载的图像的文件路径.getView 函数如下所示:

The adapter stores an ArrayList that contains the file paths of all the images that need loaded. The getView function looks like this:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ImageView iv = null;
    if(convertView == null) {
        convertView = getLayoutInflater().inflate(R.id.your_view, null); /* Inflate your view here */
        iv = convertView.findViewById(R.id.your_image_view);
    } else {
        iv = convertView.findViewById(R.id.your_image_view);
        DecodeTask task = (DecodeTask)iv.getTag(R.id.your_image_view);
        if(task != null) {
            task.cancel(true);
        }
    }
    iv.setImageBitmap(null);
    DecodeTask task = new DecodeTask(iv);
    task.execute(getItem(position) /* File path to image */);
    iv.setTag(R.id.your_image_view, task);

    return convertView;
}

注意:这里只是一个警告,在 1.5 - 2.3 版本上这可能仍然会给您带来内存问题,因为它们使用线程池来处理 AsyncTask.3.0+ 默认情况下返回串行模型以执行 AsyncTasks,这使其每次运行一个任务,从而在任何给定时间使用更少的内存.不过只要你的图片不是太大,你应该没问题.

NOTE: Just a caveat here, this might still give you memory problems on versions 1.5 - 2.3 since they use a thread pool for AsyncTask. 3.0+ go back to the serial model by default for executing AsyncTasks which keeps it to one task running at a time, thus using less memory at any given time. So long as your images aren't too big though, you should be fine.

更新:虽然此解决方案仍然有效,但开源社区已经添加了大量内容以更简洁的方式解决此问题.像 Glide 或 Picasso 这样的库都可以很好地处理加载列表中的项目,如果可能,我建议您研究其中一种解决方案.

UPDATE: While this solution will still work, there have been great additions to the open source community for solving this problem in a cleaner way. Libraries like Glide or Picasso both handle loading items in a list quite well and I'd recommend you look into one of those solutions if possible.

这篇关于Android 中包含图像的大型 ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆