简体   繁体   English

列表视图中静态图像的内存优化

[英]Memory optimization for static images in listview

  • I have 2 different list-view filed with images only 我有2个仅包含图像的列表视图

  • Best solution for memory optimization for static images in list-view 列表视图中静态图像内存优化的最佳解决方案

  • I am having memory issue every time Out of memory issue 每次出现内存不足问题

  • Every solution is regarding dynamic images or loading images from web-service 每个解决方案都涉及动态图像或从Web服务加载图像

  • What about static image ? 静态图片呢?

  • I am having about 70-80 images in listview (total) 我在列表视图中大约有70-80张图像(总计)

  • Code is not required as i am simply filling listview with images, no web-service is used. 不需要代码,因为我只是用图像填充listview,不使用任何Web服务。

code : 代码:

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);

    }

Adapter Class : 适配器类别:

public class SelectAdapter extends BaseAdapter { 公共类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);
}

} }

Log : 日志:

java.lang.OutOfMemoryError: Failed to allocate a 34560012 byte allocation with 4194304 free bytes and 14MB until OOM

You need to use BitmapFactory.Options. 您需要使用BitmapFactory.Options。 BitmapFactory.Options can be used to process Bitmap size and other properties without loading them into the memory by help of inJustDecodeBounds. BitmapFactory.Options可用于处理位图大小和其他属性,而无需借助inJustDecodeBounds将它们加载到内存中。 In order to remove OutOfMemory error, you need to load a scaled down version of the Bitmap from your resources (drawable folder). 为了消除OutOfMemory错误,您需要从资源(可绘制文件夹)中加载按比例缩小的位图版本。 This can be achieved by help of inSampleSize. 这可以通过inSampleSize来实现。 If inSampleSize > 1, it requests the decoder to load a scaled down version into the memory saving you from OutOfMemory errors. 如果inSampleSize> 1,则它要求解码器将按比例缩小的版本加载到内存中,从而避免OutOfMemory错误。

Go through the following webpage for more details: 浏览以下网页以获取更多详细信息:

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Demo code: 演示代码:

You will need the following two methods to process each bitmap or drawable file: 您将需要以下两种方法来处理每个位图或可绘制文件:

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);
}

The calculateInSampleSize method is used to calculate the inSampleSize required for each bitmap. validateInSampleSize方法用于计算每个位图所需的inSampleSize。 The resultant inSampleSize value will be the best suitable value or the best fit to scale your bitmap to your specified requirements as you will specify by help of the arguments in the very same method. 最终的inSampleSize值将是最合适的值,或者是将位图缩放到指定要求的最合适值,就像您将在完全相同的方法中借助参数指定的那样。

The method decodeSampleBitmapFromResource will decode the bitmap file from your app's resources and let you calculate the inSampleSize without allocating memory for the bitmap. 方法encodeSampleBitmapFromResource将从应用程序的资源中解码位图文件,并让您计算inSampleSize而不为位图分配内存。 The memory for the bitmap will only be allocated once the correct inSampleSize for that particular bitmap is calculated. 只有为特定位图计算了正确的inSampleSize后,才会分配该位图的内存。 This is accomplished by help of inJustDecodeBounds property for the BitmapFactory.Options object. 这是通过BitmapFactory.Options对象的inJustDecodeBounds属性帮助完成的。

Now, you just have to use these methods to add the bitmaps to your list view. 现在,您只需要使用这些方法即可将位图添加到列表视图中。 For the sake of example, lets assume you have an ImageView in each element or row of your ListView. 为了举例说明,假设您在ListView的每个元素或每一行中都有一个ImageView。 now, we will add the bitmap to the ImageView like this: 现在,我们将像这样将位图添加到ImageView中:

imageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
            resID, imageView.getMaxWidth(), imageView.getMaxHeight()));

Here, resID will be the Resource ID for your Bitmap and for the width and height I have currently used the width and height of the ImageView itself because I personally find it the best solution. 在这里,resID将是您的位图的资源ID,以及我目前使用ImageView本身的宽度和高度的宽度和高度的资源ID,因为我个人认为这是最好的解决方案。 But, you can use any value. 但是,您可以使用任何值。 Make sure, your value for width and height does not exceed the width and height of the view on which the bitmap will be placed. 确保您的width和height值不超过将放置位图的视图的宽度和高度。

Updated segment of your code: 更新的代码段:

@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;
}

Look at the last line of getView method. 查看getView方法的最后一行。 ivCard is your ImageView from your ViewHolder for your Adapter which will now use the method setImageBitmap to set the resource as a bitmap on the ImageView. ivCard是ViewHolder中用于适配器的ImageView,它现在将使用setImageBitmap方法将资源设置为ImageView上的位图。

Provided you display only 5-10 images on the screen at a time, and each image is just a few hundred kb at max, using a normal recycled list view should be enough to avoid a OOM. 如果您一次只在屏幕上显示5-10张图像,并且每张图像最多仅几百kb,那么使用正常的回收列表视图就足以避免OOM。

add all your drawable resource Ids to a listview first 首先将所有可绘制资源ID添加到列表视图

imageResList.add(R.drawable.fulllogocard1)
imageResList.add(R.drawable.fulllogocard2)
imageResList.add(R.drawable.fulllogocard3)
imageResList.add(R.drawable.fulllogocard4)
......

Then, implement your adapter as follows: 然后,按以下方式实现您的适配器:

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;
        }
    }

}

The adapter simply recycles existing listView views, so at any given point of time only the visible views (and therefore only the visible images) are rendered on the screen at a given time. 适配器仅回收现有的listView视图,因此在任何给定的时间点,在给定的时间仅在屏幕上呈现可见的视图(因此也只有可见的图像)。

This is just a workaround/hack. 这只是一个解决方法/黑客。 Ideally, you would want to use a image library built for this purpose, such as Universal image loader, glide, picasso, or fresco 理想情况下,您需要使用为此目的而构建的图像库,例如通用图像加载器,滑行,毕加索或壁画

Picasso v/s Imageloader v/s Fresco vs Glide 毕加索v / s图像加载器v / s壁画vs滑翔

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM