Android listview中静态图像的内存优化
我有2个不同的列表视图,仅包含图像Android listview中静态图像的内存优化,android,listview,out-of-memory,memory-optimization,Android,Listview,Out Of Memory,Memory Optimization,我有2个不同的列表视图,仅包含图像 列表视图中静态图像内存优化的最佳解决方案 每次内存不足时,我都会出现内存问题 每个解决方案都涉及动态图像或从web服务加载图像 静态图像呢 我在listview中有大约70-80个图像(总计) 代码不是必需的,因为我只是用图像填充listview,没有使用web服务 代码: private ListView lv; private ArrayList<Integer> cd; @Override protected void onCre
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);
}
您需要使用BitmapFactory.Options。选项可用于处理位图大小和其他属性,而无需借助inJustDecodeBounds将其加载到内存中。为了删除OutOfMemory错误,您需要从资源(drawable文件夹)中加载位图的缩小版本。这可以通过inSampleSize的帮助实现。如果inSampleSize>1,它会请求解码器将一个缩小的版本加载到内存中,以避免出现OutOfMemory错误 有关详细信息,请浏览以下网页: 演示代码:
@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;
}
处理每个位图或可绘制文件需要以下两种方法:
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上的位图。您需要使用BitmapFactory.Options。选项可用于处理位图大小和其他属性,而无需借助inJustDecodeBounds将其加载到内存中。为了删除OutOfMemory错误,您需要从资源(drawable文件夹)中加载位图的缩小版本。这可以通过inSampleSize的帮助实现。如果inSampleSize>1,它会请求解码器将一个缩小的版本加载到内存中,以避免出现OutOfMemory错误 有关详细信息,请浏览以下网页: 演示代码:
@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;
}
处理每个位图或可绘制文件需要以下两种方法:
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添加到listview
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视图,因此在任何给定的时间点,在给定的时间只有可见的视图(因此只有可见的图像)在屏幕上呈现
这只是一种变通方法。理想情况下,您可能希望使用为此目的而构建的图像库,例如Universal image loader、glide、毕加索或fresco
如果您一次只在屏幕上显示5-10个图像,并且每个图像最大只有几百kb,那么使用普通的循环列表视图应该足以避免OOM 添加所有可提取资源