Оптимизация памяти для статических изображений в виде списка
У меня есть 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.