listview中静态图片的内存优化
Memory optimization for static images in listview
我有 2 个不同的列表视图,仅包含图像
列表视图静态图片内存优化最佳方案
我每次都遇到内存问题 内存不足问题
每个解决方案都是关于动态图像或从网络服务加载图像
静态图片呢?
我在列表视图中有大约 70-80 张图像(总共)
不需要代码,因为我只是用图像填充列表视图,没有使用网络服务。
代码:
private ListView lv;
private ArrayList<Integer> cd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select);
lv = (ListView) findViewById(R.id.lv);
cd = new ArrayList<Integer>();
cd1.add(R.drawable.fuld1);
cd1.add(R.drawable.ful2);
cd1.add(R.drawable.fu4);
lv.setAdapter(new Select(this, cd1));
lv.setOnItemClickListener(this);
}
适配器Class:
public class SelectAdapter 扩展 BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private ViewHolder holder;
private ArrayList<Integer> list;
public SelectAdapter(Activity activity, ArrayList<Integer> list) {
this.activity = activity;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.list = list;
}
@Override
public int getCount() {
return 43;
}
@Override
public Object getItem(int arg0) {
return arg0;
}
@Override
public long getItemId(int arg0) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.select_item, parent,
false);
holder = new ViewHolder();
holder.iv = (ImageView) convertView
.findViewById(R.id.ivSelect;
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.iv.setBackgroundResource(list.get(position));
return convertView;
}
private class ViewHolder {
ImageView ivCard;
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}
日志:
java.lang.OutOfMemoryError: Failed to allocate a 34560012 byte allocation with 4194304 free bytes and 14MB until OOM
您需要使用 BitmapFactory.Options。 BitmapFactory.Options 可用于处理 Bitmap 大小和其他属性,而无需借助 inJustDecodeBounds 将它们加载到内存中。为了消除 OutOfMemory 错误,您需要从您的资源(可绘制文件夹)加载按比例缩小的位图版本。这可以通过 inSampleSize 的帮助来实现。如果 inSampleSize > 1,它会请求解码器将按比例缩小的版本加载到内存中,从而避免出现 OutOfMemory 错误。
查看以下网页了解更多详情:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
演示代码:
您将需要以下两种方法来处理每个位图或可绘制文件:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
calculateInSampleSize方法用于计算每个位图所需的inSampleSize。生成的 inSampleSize 值将是最合适的值或最适合将位图缩放到指定要求的值,因为您将在同一方法中借助参数指定。
方法 decodeSampleBitmapFromResource 将从您的应用程序资源中解码位图文件,并让您计算 inSampleSize 而无需为位图分配内存。只有在计算出特定位图的正确 inSampleSize 后,才会为位图分配内存。这是通过 BitmapFactory.Options 对象的 inJustDecodeBounds 属性 帮助完成的。
现在,您只需使用这些方法将位图添加到您的列表视图中。为举例起见,假设您在 ListView 的每个元素或行中都有一个 ImageView。现在,我们将像这样将位图添加到 ImageView:
imageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
resID, imageView.getMaxWidth(), imageView.getMaxHeight()));
在这里,resID 将是您的位图的资源 ID,对于宽度和高度,我目前使用 ImageView 本身的宽度和高度,因为我个人认为它是最佳解决方案。但是,您可以使用任何值。确保您的宽度和高度值不超过放置位图的视图的宽度和高度。
更新的代码段:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.select_item, parent,
false);
holder = new ViewHolder();
holder.ivCard = (ImageView) convertView
.findViewById(R.id.ivSelect);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.ivCard.setImageBitmap(decodeSampledBitmapFromResource(parent.getResources(),
list.get(position), holder.ivCard.getMaxWidth(), holder.ivCard.getMaxHeight()));
return convertView;
}
查看getView 方法的最后一行。 ivCard 是您的适配器的 ViewHolder 中的 ImageView,它现在将使用方法 setImageBitmap 将资源设置为 ImageView 上的位图。
如果你一次只在屏幕上显示 5-10 张图片,并且每张图片最大只有几百 kb,使用普通的回收列表视图应该足以避免 OOM。
首先将所有可绘制资源 ID 添加到列表视图
imageResList.add(R.drawable.fulllogocard1)
imageResList.add(R.drawable.fulllogocard2)
imageResList.add(R.drawable.fulllogocard3)
imageResList.add(R.drawable.fulllogocard4)
......
然后,按如下方式实现您的适配器:
public ImageViewAdapter extends BaseAdapter
{
@Override
public int getCount()
{
return imageList.size();
}
@Override
public Object getItem(int i)
{
return null;
}
@Override
public long getItemId(int i)
{
return 0;
}
@Override
public View getView(int i, View existingView, ViewGroup viewGroup)
{
if(existingView == null)
{
ImageView imageView = new ImageView(viewGroup.getContext());
imageView.setImageResource(imageResList.get(i));
return imageView;
}
else
{
ImageView imageView = ((ImageView) existingView);
imageView.setImageResource(imageResList.get(i));
return imageView;
}
}
}
适配器简单地回收现有的 listView 视图,因此在任何给定时间点,只有可见视图(因此只有可见图像)在给定时间呈现在屏幕上。
这只是一个workaround/hack。理想情况下,您会希望使用为此目的构建的图像库,例如 Universal image loader、glide、picasso 或 fresco
Picasso v/s Imageloader v/s Fresco vs Glide
我有 2 个不同的列表视图,仅包含图像
列表视图静态图片内存优化最佳方案
我每次都遇到内存问题 内存不足问题
每个解决方案都是关于动态图像或从网络服务加载图像
静态图片呢?
我在列表视图中有大约 70-80 张图像(总共)
不需要代码,因为我只是用图像填充列表视图,没有使用网络服务。
代码:
private ListView lv;
private ArrayList<Integer> cd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select);
lv = (ListView) findViewById(R.id.lv);
cd = new ArrayList<Integer>();
cd1.add(R.drawable.fuld1);
cd1.add(R.drawable.ful2);
cd1.add(R.drawable.fu4);
lv.setAdapter(new Select(this, cd1));
lv.setOnItemClickListener(this);
}
适配器Class:
public class SelectAdapter 扩展 BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private ViewHolder holder;
private ArrayList<Integer> list;
public SelectAdapter(Activity activity, ArrayList<Integer> list) {
this.activity = activity;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.list = list;
}
@Override
public int getCount() {
return 43;
}
@Override
public Object getItem(int arg0) {
return arg0;
}
@Override
public long getItemId(int arg0) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.select_item, parent,
false);
holder = new ViewHolder();
holder.iv = (ImageView) convertView
.findViewById(R.id.ivSelect;
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.iv.setBackgroundResource(list.get(position));
return convertView;
}
private class ViewHolder {
ImageView ivCard;
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}
日志:
java.lang.OutOfMemoryError: Failed to allocate a 34560012 byte allocation with 4194304 free bytes and 14MB until OOM
您需要使用 BitmapFactory.Options。 BitmapFactory.Options 可用于处理 Bitmap 大小和其他属性,而无需借助 inJustDecodeBounds 将它们加载到内存中。为了消除 OutOfMemory 错误,您需要从您的资源(可绘制文件夹)加载按比例缩小的位图版本。这可以通过 inSampleSize 的帮助来实现。如果 inSampleSize > 1,它会请求解码器将按比例缩小的版本加载到内存中,从而避免出现 OutOfMemory 错误。
查看以下网页了解更多详情:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
演示代码:
您将需要以下两种方法来处理每个位图或可绘制文件:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
calculateInSampleSize方法用于计算每个位图所需的inSampleSize。生成的 inSampleSize 值将是最合适的值或最适合将位图缩放到指定要求的值,因为您将在同一方法中借助参数指定。
方法 decodeSampleBitmapFromResource 将从您的应用程序资源中解码位图文件,并让您计算 inSampleSize 而无需为位图分配内存。只有在计算出特定位图的正确 inSampleSize 后,才会为位图分配内存。这是通过 BitmapFactory.Options 对象的 inJustDecodeBounds 属性 帮助完成的。
现在,您只需使用这些方法将位图添加到您的列表视图中。为举例起见,假设您在 ListView 的每个元素或行中都有一个 ImageView。现在,我们将像这样将位图添加到 ImageView:
imageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
resID, imageView.getMaxWidth(), imageView.getMaxHeight()));
在这里,resID 将是您的位图的资源 ID,对于宽度和高度,我目前使用 ImageView 本身的宽度和高度,因为我个人认为它是最佳解决方案。但是,您可以使用任何值。确保您的宽度和高度值不超过放置位图的视图的宽度和高度。
更新的代码段:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.select_item, parent,
false);
holder = new ViewHolder();
holder.ivCard = (ImageView) convertView
.findViewById(R.id.ivSelect);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.ivCard.setImageBitmap(decodeSampledBitmapFromResource(parent.getResources(),
list.get(position), holder.ivCard.getMaxWidth(), holder.ivCard.getMaxHeight()));
return convertView;
}
查看getView 方法的最后一行。 ivCard 是您的适配器的 ViewHolder 中的 ImageView,它现在将使用方法 setImageBitmap 将资源设置为 ImageView 上的位图。
如果你一次只在屏幕上显示 5-10 张图片,并且每张图片最大只有几百 kb,使用普通的回收列表视图应该足以避免 OOM。
首先将所有可绘制资源 ID 添加到列表视图
imageResList.add(R.drawable.fulllogocard1)
imageResList.add(R.drawable.fulllogocard2)
imageResList.add(R.drawable.fulllogocard3)
imageResList.add(R.drawable.fulllogocard4)
......
然后,按如下方式实现您的适配器:
public ImageViewAdapter extends BaseAdapter
{
@Override
public int getCount()
{
return imageList.size();
}
@Override
public Object getItem(int i)
{
return null;
}
@Override
public long getItemId(int i)
{
return 0;
}
@Override
public View getView(int i, View existingView, ViewGroup viewGroup)
{
if(existingView == null)
{
ImageView imageView = new ImageView(viewGroup.getContext());
imageView.setImageResource(imageResList.get(i));
return imageView;
}
else
{
ImageView imageView = ((ImageView) existingView);
imageView.setImageResource(imageResList.get(i));
return imageView;
}
}
}
适配器简单地回收现有的 listView 视图,因此在任何给定时间点,只有可见视图(因此只有可见图像)在给定时间呈现在屏幕上。
这只是一个workaround/hack。理想情况下,您会希望使用为此目的构建的图像库,例如 Universal image loader、glide、picasso 或 fresco
Picasso v/s Imageloader v/s Fresco vs Glide