Существует ли замена для виджета Галерея с утилизацией View?
Стандартный виджет "Галерея" на Android не изменяет представления - каждый раз, когда вызывается представление для новой позиции, виджет всегда вызывает getView
метод адаптера с convertView
установить на ноль.
Когда вы прокручиваете назад и вперед, это приводит к тому, что создается много представлений, которые, по-видимому, перерабатывает компонент рециркуляции, в котором их хранит Галерея, и приводит к ситуации с OOM.
Вы можете легко проверить это с помощью нескольких изображений большого размера в качестве элементов галереи, но только TextView вызовет это в конце. Поместите запись журнала со счетчиком в getView
Метод вашего адаптера также, чтобы увидеть, сколько новых видов создаются.
Существует ли сторонний виджет, который ведет себя как галерея, но также реализует переработку вида?
5 ответов
В конце концов, мое решение заключалось в предложении @CommonsWare изменить исходный код галереи. Для этого также необходимо скопировать следующие файлы:
AdapterView
AbsSpinner
но это довольно просто.
После этого я изменил код, сделав следующее:
RecycleBin
(AbsSpinner
)
- Размещайте предметы в утилизаторе один за другим, а не в соответствии с положением
- Извлекать предметы из нижней части рециркулятора, независимо от запрошенной позиции
- Существующая реализация предполагала, что каждая отдельная позиция в адаптере приводила к уникальному представлению. Вышеуказанные изменения хороши только в том случае, если ваша Галерея содержит только один тип элемента, в противном случае вам нужно будет добавить какой-то ключ на основе типа элемента и требуемого количества этого типа.
Gallery
- Используется отражение (тьфу), чтобы изменить приват
mGroupFlags
переменнаяViewGroup
чтобы разрешить повторное упорядочение дочерних элементов - я также установил логическое значение, указывающее, успешно ли получен доступ к полю, который я тестировал перед использованием компонента.- Убраны все звонки на
mRecycler.clear()
- Число элементов, которые галерея должна отображать, изменяет при прокрутке, и существующая реализация очищает утилиту при вызове (a) setSelection (b) произошла прокрутка движения
С этими модификациями мой счетчик у меня newView
метод в моем адаптере достиг... 7.
Вот код (размещен в открытом доступе 2013/08/07 под http://en.wikipedia.org/wiki/WTFPL)
На самом деле есть альтернатива, хотя я лично не проверял ее:
Очень поздно для вечеринки, но я изменил EcoGallery, чтобы сделать еще несколько вещей (и избежать некоторых сбоев).
Я назвал это TimelineGallery, и это то же самое дерьмо, что и Gallery, но он может выполнять плавную прокрутку и не делает странных вещей, когда изображения загружаются асинхронно.
Чтобы продемонстрировать это, в примере используются Picasso и PullToRefresh.
Оригинальный код, авторские права и тому подобное принадлежит Google, поэтому вините их за создание такого дерьмового виджета.
Последнее замечание: я не рекомендую использовать галерею, она старая, глючная, взломанная и, вероятно, никогда не будет поддерживаться. Проблема не в том, чтобы исправить ошибки, а в том, что вся архитектура Галереи неверна и, как таковая, исправить ее невозможно без введения дополнительных хаков.
Google понял это и осудил это. Используйте ViewPager или HorizontalScrollList и разберитесь с ограничениями каждого из них.
Если вы все еще хотите пойти дальше и использовать эту "галерею", не стесняйтесь, она работает, но она может привести к сбою вашего приложения и может вас расстроить.
Я использовал патч от http://code.google.com/p/android/issues/detail?id=3376
Еще один более быстрый способ обойти проблемы 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;
}
Конечно, теперь возможно, что вы увидите разные разрешения одного и того же изображения в разное время - но, по крайней мере, ваша Галерея больше не будет падать, и вы всегда будете показывать самое высокое разрешение, какое только сможете.