Оптимизация памяти для статических изображений в виде списка

  • У меня есть 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);

    }

Класс адаптера:

открытый класс 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 ответа

Решение

Вам нужно использовать BitmapFactory.Options. BitmapFactory.Options можно использовать для обработки размера растрового изображения и других свойств без загрузки их в память с помощью 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);
}

Метод calcInSampleSize используется для вычисления inSampleSize, требуемого для каждого растрового изображения. Результирующее значение inSampleSize будет наилучшим подходящим значением или наилучшим соответствием для масштабирования вашего растрового изображения в соответствии с вашими заданными требованиями, как вы укажете с помощью аргументов в том же методе.

Метод decodeSampleBitmapFromResource будет декодировать файл растрового изображения из ресурсов вашего приложения и позволит вам вычислить inSampleSize без выделения памяти для растрового изображения. Память для растрового изображения будет выделена только после вычисления правильного inSampleSize для этого конкретного растрового изображения. Это достигается с помощью свойства inJustDecodeBounds для объекта BitmapFactory.Options.

Теперь вам просто нужно использовать эти методы для добавления растровых изображений в список. Для примера, давайте предположим, что у вас есть ImageView в каждом элементе или строке вашего ListView. Теперь мы добавим растровое изображение в ImageView следующим образом:

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

Здесь resID будет идентификатором ресурса для вашего растрового изображения, а также для ширины и высоты. В настоящее время я использовал ширину и высоту самого 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 - это ваш ImageView из вашего ViewHolder для вашего адаптера, который теперь будет использовать метод setImageBitmap, чтобы установить ресурс как растровое изображение в ImageView.

При условии, что вы выводите на экран только 5-10 изображений за раз, а каждое изображение - максимум несколько сотен килобайт, достаточно обычного просмотра списка с перезаписью, чтобы избежать OOM.

сначала добавьте все свои идентификаторы ресурсов в список

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, picasso или fresco.

Picasso v/s Imageloader v/s Fresco vs Glide

Другие вопросы по тегам