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
我需要将可绘制文件夹中的一堆图像加载到图像视图列表中。我收到 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