listview

时间:2016-03-19 09:37:52

标签: android listview out-of-memory memory-optimization

  • 我有两个不同的列表视图,只有图像

  • 列表视图中静态图像内存优化的最佳解决方案

  • 每次内存不足时都会出现内存问题

  • 每个解决方案都涉及动态图像或从网络服务加载图像

  • 静态图像怎么样?

  • 我在listview中有大约70-80张图片(总计)

  • 不需要代码,因为我只是用图片填充listview,没有使用网络服务。

代码:

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

    }

适配器类:

公共类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

2 个答案:

答案 0 :(得分:1)

您需要使用BitmapFactory.Options。 BitmapFactory.Options可用于处理位图大小和其他属性,而无需借助inJustDecodeBounds将它们加载到内存中。为了删除OutOfMemory错误,您需要从资源(可绘制文件夹)加载缩小版的Bitmap。这可以通过inSampleSize的帮助来实现。如果inSampleSize&gt; 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将是您的Bitmap的资源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上的位图。

答案 1 :(得分:1)

如果您一次只在屏幕上显示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视图,因此在任何给定的时间点,只有可见视图(因此只有可见图像)在给定时间在屏幕上呈现。

这只是一种解决方法/黑客攻击。理想情况下,您可能希望使用为此目的而构建的图像库,例如通用图像加载器,滑动,毕加索或壁画

Picasso v/s Imageloader v/s Fresco vs Glide