Существует ли замена виджета Gallery на View recycling?

#android #view #gallery #out-of-memory #recycle

#Android #Вид #Галерея #нехватка памяти #переработать

Вопрос:

Виджет Gallery по умолчанию на Android не перерабатывает просмотры — каждый раз, когда вызывается view для новой позиции, виджет всегда вызывает getView метод адаптера с convertView значением null.

При прокрутке назад и вперед создается множество просмотров, которые компонент recycler, в котором они хранятся в галерее, похоже, не перерабатывает достаточно быстро, что приводит к ситуации с ООМ.

Вы можете легко протестировать это с помощью нескольких изображений большого размера в качестве элементов вашей галереи, но в конечном итоге это приведет только к TextView. Также поместите оператор log со счетчиком в getView метод вашего адаптера, чтобы увидеть, сколько новых просмотров создано.

Существует ли сторонний виджет, который ведет себя как галерея, но который также реализует переработку вида?

Комментарии:

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

2. Приветствия @CommonsWare — Я уже начал это делать, но это некрасиво и означает копирование и редактирование довольно большого количества классов, поскольку в нем используется несколько защищенных элементов и методов, поэтому мне просто интересно, есть ли у кого-нибудь идеи получше.

Ответ №1:

В конце концов, я принял решение, согласившись с предложением @CommonsWare изменить исходный код Gallery. Для этого также требуется скопировать следующие файлы:

  • AdapterView
  • AbsSpinner

но это довольно просто.

После этого я изменил код, чтобы выполнить следующее:

RecycleBin ( AbsSpinner )

  • Размещайте объекты в утилизаторе один за другим, а не в соответствии с положением
  • Извлекайте объекты из нижней части утилизатора, независимо от запрошенной позиции
  • Существующая реализация предполагала, что каждая отдельная позиция в адаптере приводила к уникальному представлению. Вышеуказанные изменения хороши только в том случае, если ваша галерея содержит только один тип элементов, в противном случае вам нужно будет добавить какой-то ключ, основанный на типе элемента и требуемом количестве этого типа

Gallery

  • Использовал отражение (тьфу), чтобы изменить закрытую mGroupFlags переменную ViewGroup , чтобы разрешить повторный порядок дочерних элементов — я также установил логическое значение, указывающее, был ли выполнен успешный доступ к полю, который я тестирую перед использованием компонента.
  • Удалены все вызовы для mRecycler.clear()
  • Количество элементов, которые галерея должна отображать, изменяется по мере прокрутки, и существующая реализация очистит переработчик при (а) вызове setSelection (б) возникновении прокрутки движения

С этими изменениями мой счетчик в моем newView методе в моем адаптере достиг … 7.

Вот код (Размещен в общественном достоянии 08.03.07 под http://en.wikipedia.org/wiki/WTFPL )

Комментарии:

1. не могли бы вы опубликовать ссылку на исходные файлы для Gallery и двух других, которые вы упомянули?

2. Слишком долго публиковать все классы в одном ответе, поэтому я поместил их здесь: pastebin.com/FWyYTt4D

3. Огромное спасибо за публикацию этого! Он отлично работает по сравнению со встроенным виджетом Gallery. Жаль, что нам приходится прибегать к таким крайним мерам только для того, чтобы получить работающий ресайклер. Если бы я понимал, какой это будет болью, я бы выбрал другой виджет вместо Gallery. Ваша измененная галерея работает и решила мою проблему, хотя еще раз спасибо!

4. я не могу использовать предоставленный вами код … существует несколько исключений приведения к классу, а также OnItemClickListener также создает проблемы … 03-05 12:16:00.545: E / AndroidRuntime (30246): java.lang. ClassCastException: android.widget. Галерея $LayoutParams 03-05 12:16:00.545: E / AndroidRuntime(30246): в .EcoGallery.setUpChild(EcoGallery.java:773) 03-05 12:16:00.545: E / AndroidRuntime(30246): на com.exiticlabs.arsenallwp.EcoGallery.makeAndAddView (EcoGallery.java:752) 03-05 12:16:00.545: E / AndroidRuntime(30246): в .EcoGallery.layout(EcoGallery.java:646) .EcoGallery.onLayout(EcoGallery.java: 362)

5. @Akos Поскольку вы спросили, я размещаю код в открытом доступе под en.wikipedia.org/wiki/WTFPL

Ответ №2:

На самом деле есть альтернатива, хотя я лично ее не тестировал:

https://github.com/falnatsheh/EcoGallery

Комментарии:

1. EcoGallery основана на (устаревшей) Android Gallery, и это в основном исходный код Android Gallery с некоторыми изменениями (но в нем присутствуют те же ошибки, что и в старой галерее), хотя он лучше перерабатывает view. Но проблема с «прыгающей» галереей все еще существует, если вы загружаете изображения в фоновом режиме.

2. не уверен, в чем ошибка «jumping», но мне все равно не нравится вид галереи, и вы правы, что этот проект основан на исходном коде галереи, но они утверждают, что пытаются исправить этот вид. я думаю, что, по крайней мере, механизм view-recycling там исправлен.

3. Да, эта версия галереи выполняет лучшую (хотя и не «такую хорошую, как listview») работу по переработке просмотров. Самая большая проблема (которая была у оригинальной галереи и никогда не была исправлена даже после того, как она устарела) заключается в том, что когда вы загружаете свои изображения в фоновом режиме (как и следовало бы), они запускают макет, и это заставляет галерею привязывать «ближайшее изображение» к центру. это приводит к постоянному скачку при прокрутке и загрузке элементов (и размещении в галерее). Есть несколько обходных путей, которые устраняют проблему, но это халтурно. После загрузки изображений галерея работает нормально 🙂

4. Я все еще не уверен, о чем вы говорите. Если загружаются изображения и размеры просмотров основаны на них, конечно, это приведет к возникновению странных вещей, поскольку размеры просмотров меняются. это произойдет и для ListViews. вы должны использовать фиксированный размер элементов, чтобы избежать такой вещи. В любом случае, что касается библиотеки, я попробовал ее около месяца назад и отказался от нее из-за обнаруженного сбоя (происходит только после многократной быстрой прокрутки). Вот почему я думаю, что если вам необходимо использовать галерею, используйте вместо нее эту.

5. «это произойдет и для ListViews. вы должны использовать фиксированный размер элементов, чтобы избежать такой вещи.» Хммм нет, этого не происходит при просмотре списка. Откуда у вас эта идея? Это происходит потому, что исходная галерея привязывает элемент к center onLayout (который вызывается, когда ImageView выполняет setXXXXBackground(). Это была ошибка в галерее навсегда и, возможно, одна из многих причин, по которой Google решил просто отказаться от галереи и начать с нуля с (хорошо, но не на 100% похожих альтернатив) ViewPager и HorizontalScrollableView).

Ответ №3:

Я использовал патч от http://code.google.com/p/android/issues/detail?id=3376#c19

Ответ №4:

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

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

Чтобы продемонстрировать это, в примере используются Picasso и PullToRefresh.

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

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

Google понял это и устарел. Используйте ViewPager или Horizontalscrollist и учитывайте ограничения каждого из них.

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

Ответ №5:

Другой более быстрый способ решения проблем с OutOfMemory — попробовать / перехватить код, в котором вы декодируете изображение, и, если выдается исключение OutOfMemory, вы пытаетесь декодировать его с меньшим разрешением снова..

что-то вроде этого:

 private static Bitmap decodeFile(File f, int size, int suggestedScale) {

    int scale = 1;
    Bitmap bmp = null;
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;

        if(suggestedScale > 0)
            scale = suggestedScale;
        else {
            if (width_tmp >= height_tmp) {
                scale = Math.round((float)(width_tmp) / size);
            } else {
                scale = Math.round((float)(height_tmp) / size);
            }
        }

        if(scale < 2)
            return BitmapFactory.decodeFile(f.getPath()); 

        Debug.i(TAG, "width: "   width_tmp   "  height: "   height_tmp   "  scale: "   scale);


        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {

    } catch(OutOfMemoryError e) {
        Debug.i(TAG, "we retry it cause of an OutOfMemoryException");
        return decodeFile(f, size, scale 1);
    } catch(Exception e){
        Debug.w(TAG, e);
    }
    return bmp;
}
  

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