Android listview中静态图像的内存优化

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

我有2个不同的列表视图,仅包含图像

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

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

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

  • 静态图像呢

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

  • 代码不是必需的,因为我只是用图像填充listview,没有使用web服务

  • 代码:

    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

    添加所有可提取资源