Viewpager с большими растровыми изображениями

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

Я пытаюсь заставить его работать с большими растровыми изображениями, поэтому я использую алгоритм для масштабирования растрового изображения и установки его для просмотра изображения каждой страницы. Алгоритму требуется ширина/высота ImageView (для уменьшения).

У меня проблема в том, что при выполнении моего пользовательского метода PagerAdapter ширина/высота ImageView еще не известна (getWidth/getHeight возвращает 0), поэтому он не работает:

public Object instantiateItem(ViewGroup collection, int position) {
        LayoutInflater inflater = (LayoutInflater) collection.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.document_page, null);
        ((ViewPager) collection).addView(view, 0);

        ImageView imageView = (ImageView) view.findViewById(R.id.imageView);

        // Obtain the image file URI
        // Call algorithm to get scaled bitmap using ImageView width and height --> PROBLEM: imageView.getWidth()/Height() return 0!!
        // Set ImageView with scaled bitmap to avoid OutOfMemory Exception

        return view;
}

Что ты посоветуешь?

Спасибо.


person fernandospr    schedule 17.01.2013    source источник
comment
Этот вопрос только что снова пришел мне в голову, когда я смотрел два небольших урока Чета Хаасе. Они только что загружены и охватывают inSampleSize (youtube.com/watch?v=12cB7gnL6po) и производительность загрузки растровых изображений в целом (youtube.com/watch?v=rsQet4nBVi8 ). Обязательно проверьте, можете ли вы использовать некоторые знания для оптимизации кода и предотвращения исключения.   -  person saschoar    schedule 01.02.2013
comment
Наконец, вы решаете это? у меня такая же проблема   -  person Hanzo    schedule 17.06.2014


Ответы (4)


Масштабирование изображения на стороне клиента:

Вам не нужен собственный алгоритм для масштабирования изображения, если оно уже есть на вашем устройстве. ImageView имеет ScaleType, который можно установить следующим образом:

imageView.setScaleType(ImageView.ScaleType.CENTER_CROP)

(или в XML с атрибутом android:scaleType).

Остается вопрос, как получить изображение с вашего URL для отображения в ImageView. Используйте такие библиотеки, как SmartImageView или Android-Query (более мощный), чтобы добиться этого асинхронно (т. е. без блокировки потока пользовательского интерфейса).

Например, с помощью SmartImageView вы можете использовать что-то вроде этого:

myImageView.setImageUrl("http://www.awesomeimages.com/myawesomeimage.jpg");

Просто прочитайте примеры на этой странице, там гораздо больше вариантов.

Масштабирование на стороне сервера

Если у вас есть какой-то алгоритм изменения размера, который запускается, например. по определенным параметрам URL, используйте это решение и передайте ширину и высоту по своему желанию (возможно, с помощью одного из вышеупомянутых библиотеки).

Поскольку вы сказали, что getWidth() и getHeight() возвращают 0, посмотрите на layout_width и layout_height, которые вы установили для ImageView. Если это fill_parent, проблем быть не должно, но если оно должно быть wrap_content, рассмотрите возможность использования локального прозрачного фиктивного изображения с тем же соотношением сторон, что и у окончательного изображения (пока «реальное изображение» не будет набор).

person saschoar    schedule 17.01.2013
comment
Спасибо, но я использую изображения из SDCard, поэтому файлы нужно декодировать с помощью BitmapFactory.decodeFile. decodeFile получает параметр для указания некоторых опций, одна из них — inSampleSize, которая используется для уменьшения масштаба. Если я не использую это, я могу получить OutOfMemoryException. Алгоритм, который я использую, объясняется здесь: developer.android.com /обучение/отображение растровых изображений/ - person fernandospr; 18.01.2013
comment
хорошо, тогда вы попробовали мое второе решение? ImageView, у которого ширина или высота установлены на fill_parent, обычно возвращает правильный размер в пикселях на getWidth() или getHeight(). В противном случае этот подход с фиктивным изображением может быть полезен? - person saschoar; 18.01.2013

Используйте фабрику растровых изображений с параметром justInbounds. Таким образом, он просто вернет размер растрового изображения, не загружая растровое изображение в оперативную память.

  options.inJustDecodeBounds = true;
person Ercan    schedule 17.01.2013
comment
Извините, но это не имеет отношения к моей проблеме. Мне нужно знать ширину и высоту моего изображения, а не растрового изображения. - person fernandospr; 01.02.2013
comment
Извините, я неправильно понял. Вы можете попробовать getMeasuredHeight() или получить эти значения, переопределив метод onLayout() изображения. - person Ercan; 01.02.2013

попробуйте сэмплировать растровое изображение по размеру экрана (используйте inJustDecodeBounds для получения разрешения растрового изображения и displayMatrics для того, чтобы получить разрешение экрана).

если прозрачность отсутствует, вы также можете использовать формат RGB 565 декодирования растрового изображения вместо 8888 . это сэкономит около 1/4 используемой памяти.

в любом случае, я предлагаю прочитать это руководство по работе с растровыми изображениями.

person android developer    schedule 17.01.2013
comment
Я прочитал этот учебник, алгоритм, который я упоминаю, - это тот. Как видите, описанный здесь метод calculateInSampleSize имеет параметры reqWidth и reqHeight. Это должны быть ширина и высота изображения, в этом проблема. У моего изображения нет фиксированной ширины/высоты, оно использует fill_parent. И getWidth/getHeight изображения возвращает 0. - person fernandospr; 01.02.2013

Я столкнулся с той же проблемой, но нашел решение: создайте функцию и вызовите ее из «public Object instanceItem (коллекция ViewGroup, int position)».

Хитрость в том, что созданная функция будет настроена на пост-выполнение через несколько миллисекунд (для этого вы будете использовать объект Handler)

Таким образом, «instantiateItem» уже будет выполнен, а элементы представления будут иметь установленные значения ширины и высоты.

Ниже приведен пример кода о том, как действовать:

@Override
public Object instantiateItem(ViewGroup container, int position)
{
    View view =  inflater.inflate(R.layout.pager_list_item,container,false);

    TextView textView= (TextView) view.findViewById(R.id.pagerTextView);
    ImageView imageView = (ImageView)view.findViewById(R.id.pagerImageView);

    ... //stuff that don't need the width and height

    Handler handle = new Handler();
    handle.postDelayed(new Runnable()
    {
        //here the widths and heights values should be already set
       //now you can test the view.getWidth() and view.getHeight()

       if(view.getWidth()!=0 && view.getHeight()!=0)
       {
          //now you sare safe to call the android algorithm for scaling large bitmaps
       }

    }, 100); //it will wait 100 miliseconds to execute

  return view;
}

100 миллисекунд - произвольное число, может быть, слишком много!!

Я рекомендую вам использовать цикл или рекурсивную функцию, чтобы гарантировать, что блок внутри оператора «if» будет выполнен, таким образом, вы можете использовать даже меньшие миллисекунды.

В моем проекте я использовал рекурсивную функцию, которая вызывала себя до тех пор, пока не выполнялся блок «если», и я установил 5 миллисекунд.

Оно должно работать без циклов или рекурсивных методов, просто установив произвольное число, например 100, и протестировав позже, но вы не можете гарантировать, что приложение будет вести себя одинаково на всех устройствах, поскольку одно может быть быстрым, а другое может занять некоторое время. еще немного, чтобы вид был готов, вы никогда не знаете.

person Felipe    schedule 06.04.2019