Android 如何有效地将可绘制图像加载到图像视图

Android how to load drawable images to imageview efficiently

我需要将可绘制文件夹中的一堆图像加载到图像视图列表中。我收到 java.lang.OutOfMemoryError:无法分配 17280012 字节的分配,其中有 8452164 个可用字节和 8MB,直到出现 OOM 错误。

这是一个数组文件,列出了需要加载的可绘制文件夹中的所有图像名称。

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
<string-array name="photo_frames">
    <item>pf1</item>
    <item>pf2</item>
    <item>pf3</item>
    <item>pf4</item>
    <item>pf5</item>
    <item>pf6</item>
    <item>pf7</item>
    <item>pf8</item>
    <item>pf9</item>
    <item>pf10</item>
    <item>pf11</item>
    <item>pf12</item>
     </string-array>
 </resources>

这是listAdapter加载图片的代码,photoFrames是一个保存图片名称的数组。

public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        //Layout inflate for list item
        convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item_photo_frames, null);
    }

    ImageView imgPhotoFrames = (ImageView) convertView.findViewById(R.id.image_photo_frames);
    imgPhotoFrames.setImageResource(mContext.getResources().getIdentifier(photoFrames[position], "drawable", mContext.getPackageName()));


    return convertView;
}

如果我有几张图片,它可以正常加载,但有点滞后。当有更多图片要加载到 imageView 时,return 出错,谢谢。

17280012 字节相当于 2078 x 2078 像素的图像。 这大于大多数设备屏幕的分辨率。您应该一次加载这些图像中的 0-1 个,而不是 12 个。

如果您的图像具有这样的高分辨率,请降低其分辨率。

如果您的图像已经具有较低的分辨率,但您将它们放在 res/drawable/ 中,请将它们移至 res/drawable-nodpi/ 或计划为不同密度创建不同版本的可绘制对象。没有位图属于 res/drawable/,因为它只是 res/drawable-mdpi/ 的同义词,因此 Android 将在更高密度的屏幕上重新采样图像。

这取决于你的图片有多大,Android 似乎比你在电脑上读取大小时分配的图片大小 space 多一点。所以...

  • 您可以使用较小的图片。
  • Google 实际上已经发布了避免 OutOfMemoryErrors 的指南 here 这会有很大帮助,尽管我不得不使用较小的图像尺寸 还有。
  • 一种几乎肯定有效的方法是设置 android:largeHeap="true" 在您的清单中,在您的应用程序之间 标签。这会增加您的堆大小,但可能会使您的应用程序滞后 很少。
  • 您可以尝试使用 Glide,Google 推荐的库来加载图像

也许对你来说,结合这些想法会奏效。

你也可以使用缓存机制。

下面是一个示例,可能会让您对使用图像缓存有所了解:

public class FlowerAdapter extends ArrayAdapter<Flower> {

    private Context context;
    private List<Flower> flowerList;

    //set the LRU (Least Recently Used) object as an image cache
    private LruCache<Integer,Bitmap> imageCache;

    public FlowerAdapter(Context context, int resource, List<Flower> objects) {
        super(context, resource, objects);
        this.context = context;
        this.flowerList = objects;

        //initialize the cache in the constructor
        //determine the maximum amount of memory of the device
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory());
        //set the cache size to be one-eight of the maximum memory
//      final int cacheSize = maxMemory/9999999; //using this number you can see the effects
        final int cacheSize = maxMemory/8; //real-life situation
        //create the image cache (instantiate it)
        imageCache = new LruCache<>(cacheSize);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = 
                (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.item_flower, parent, false);

        //Display flower name in the TextView widget
        Flower flower = flowerList.get(position);
        TextView tv = (TextView) view.findViewById(R.id.textView1);
        tv.setText(flower.getName());

        //Display flower photo in ImageView widget
        //verify if the flower exists in cache
        Bitmap bitmap = imageCache.get(flower.getProductId());
        if (bitmap != null) {
            ImageView image = (ImageView) view.findViewById(R.id.imageView1);
            image.setImageBitmap(bitmap);
        }
        else {
            FlowerAndView container = new FlowerAndView();
            container.flower = flower;
            container.view = view;

            ImageLoader loader = new ImageLoader();
            loader.execute(container);
        }


        return view;
    }

    class FlowerAndView {
        public Flower flower;
        public View view;
        public Bitmap bitmap;
    }

    private class ImageLoader extends AsyncTask<FlowerAndView, Void, FlowerAndView> {

        @Override
        protected FlowerAndView doInBackground(FlowerAndView... params) {

            FlowerAndView container = params[0];
            Flower flower = container.flower;

            try {
                String imageUrl = MainActivity.PHOTOS_BASE_URL + flower.getPhoto();
                InputStream in = (InputStream) new URL(imageUrl).getContent();
                Bitmap bitmap = BitmapFactory.decodeStream(in);
                flower.setBitmap(bitmap);
                in.close();
                container.bitmap = bitmap;
                return container;
            } catch (Exception e) {
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(FlowerAndView result) {
            ImageView image = (ImageView) result.view.findViewById(R.id.imageView1);
            image.setImageBitmap(result.bitmap);
//          result.flower.setBitmap(result.bitmap);
            //put the image in the cache
            imageCache.put(result.flower.getProductId(),result.bitmap);
        }

    }

}

您可以为可绘制对象使用一个 id 数组

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer-array name="photo_frames">
        <item>@drawable/pf1</item>
        <item>@drawable/pf2</item>
        .....
    </integer-array>
</resources>

要加载它们,您可以使用 TypedArray

// load array into CONSTRUCTOR
TypedArray photoFrameArray = getResources().obtainTypedArray(R.array.photo_frames);

// or set you ImageView's resource to the id
imgPhotoFrames.setImageResource(photoFrameArray.getResourceId(position, R.drawable.default_drawable));

记住你应该回收数组

photoFrameArray.recycle();

此外,如果您有非常大的图像 (1000x1000 像素),您将遇到内存问题

为了加载大图,我推荐你使用Picasso。您可以将大型可绘制对象加载到 ImageView