Проблемы, вызванные этой ошибкой:
Похоже, здесь есть ошибка:
/* com.nostra13.universalimageloader.core.ImageLoader:193 */
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageView, configuration.maxImageWidthForMemoryCache, configuration.maxImageHeightForMemoryCache);
String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);
В первый раз, когда представления не создаются, он генерирует ключ кэша изображений с screenWidth x screenHeight.
И когда представления уже созданы, он генерирует новый ключ кэша изображений viewWidth x viewHeight.
Поскольку ключи оба раза различаются, изображения считаются разными, поэтому одно и то же изображение загружается в память дважды.
Итак, если у нас есть сетка с 20 изображениями, мы получим:
Его очень легко воспроизвести с помощью сэмплов, поставляемых с UIL:
Если кому-то нужно быстрое исправление, замените генератор ключей кеша статическим uri:
/* com.nostra13.universalimageloader.core.ImageLoader:194 */
String memoryCacheKey = uri; // MemoryCacheUtil.generateKey(uri, targetSize);
В первый раз, когда представления не создаются, он генерирует ключ кэша изображений с screenWidth x screenHeight.
И когда представления уже созданы, он генерирует новый ключ кэша изображений viewWidth x viewHeight.
Ты прав. Первый вид еще не отрисован и его размер неизвестен. Итак, UIL принимает размеры экрана. И во второй раз представления уже имеют свои размеры, а UIL загружает в память другие растровые изображения, которые обычно меньше. Таким образом, UIL пытается сэкономить память. Не употребляйте.
На самом деле ошибки с ключами кеша нет. Но я пока не нашел решения этой «ошибки».
Спасибо за ответ.
Ошибка в том, что вы дважды кешируете одно и то же изображение из-за неправильного ключа, и этого не должно происходить.
Я считаю, что это довольно серьезная ошибка, которая существует уже больше года.
В результате возникают проблемы с производительностью:
Кстати, можно ли настроить UIL для отображения изображения «как есть»?
Эта «ошибка» была представлена 1 марта здесь - https://github.com/nostra13/Android-Universal-Image-Loader/commit/50751a0 - в качестве улучшения. Это был запрос на перенос, и я его принял.
view.getWidth()
и view.getHeight()
(фактический размер просмотра) до этого не учитывались. Итак, учитывая эти значения, эта «ошибка» происходит чаще (на самом деле это могло случиться и раньше, но очень редко).
Чтобы предотвратить мерцание и улучшить определение размера представления, вы должны установить параметры layout_width
и layout_height
если это возможно. В противном случае вам следует установить maxWidth
и maxHeight
приблизительно.
В любом случае, если вы хотите предотвратить двойное кеширование, вы должны удалить с учетом view.getWidth()
и view.getHeight()
из UIL. Фактически вы можете сделать это, переопределив ImageViewAware
(методы getWidth()
и getHeight()
) начиная с 1.9.0.
Привет, ребята. Я решил эту ошибку с помощью обходного пути в нашем приложении (я также думаю о том, чтобы сделать запрос на перенос с этим исправлением):
мы сохраняем URL-адрес, загруженный в каждый просмотр изображения. таким образом мы избегаем запуска всего процесса, если загружаемый URL-адрес совпадает. это предотвратило проблему мерцания и помогло сэкономить несколько циклов процессора в основном потоке (каждая экономия хороша).
Думаю, эту проблему можно решить следующим образом (1.9.0):
ImageAware imageAware = new ImageViewAware(imageView, false);
imageLoader.displayImage(imageUri, imageAware);
Но пока не тестировал.
Хорошо, я изучил этот вопрос и знаю причину мерцания и некоторые способы его избежать.
Эта проблема актуальна, если ваши ImageView не имеют конкретного определенного размера ( android:layout_width
или android:layout_height
не устанавливаются со значением в падениях).
Причина мерцания в том, что ImageLoader по умолчанию определяет фактический размер ImageView. Когда вы показываете изображения в первый раз, ImageView еще не отрисовываются, поэтому они не имеют размера. Таким образом, ImageLoader создает растровые изображения в соответствии с размером экрана устройства. Затем происходит повторное использование представления, и ImageView уже нарисован и имеет определенный размер. Поэтому ImageLoader повторно декодирует изображение для создания растрового изображения в соответствии с этим размером. Новое растровое изображение меньше обычного, поэтому экономит память. Но создание нового растрового изображения занимает некоторое время, поэтому вы можете увидеть мерцание.
Есть 3 способа избежать этой проблемы:
1) Задайте параметры android:layout_width
и android:layout_height
для ImageView в виде провалов ('wrap_content' и 'match_parent' недопустимы)
2) Вызов ImageLoader после отрисовки ImageView (в imageView.post(...)
:
imageView.post(new Runnable() {
<strong i="17">@Override</strong>
public void run() {
imageLoader.displayImage(imageUri, imageView);
}
});
3) Передайте ImageViewAware
(вместо ImageView
), который не учитывает фактический размер представления:
Вместо:
imageLoader.displayImage(imageUri, imageView);
сделать следующее:
ImageAware imageAware = new ImageViewAware(imageView, false)
imageLoader.displayImage(imageUri, imageAware);
Таким образом предотвращается экономия памяти, поскольку ImageLoader сохраняет растровые изображения большего размера, чем они должны быть обычно.
На данный момент это решения, но я подумаю об улучшении UIL, чтобы справиться с этим случаем.
Да, именно так и происходит.
ImageView, используемый в сетках или списках, почти никогда не имеет определенного размера.
Так что это более распространенное использование, чем вы думаете.
Вы также можете увидеть эту ошибку в приложении примеров UIL.
Что делать, если меня не волнуют размеры и я хочу сохранить изображения как есть, почему изменение размера принудительно, а ключи содержат размер изображения в ключах кеша? Не следует ли изменять размер как часть конфигурации или что-то в этом роде?
Так что это более распространенное использование, чем вы думаете.
Я не думаю, что это используется редко. Я предложил решения.
Что делать, если меня не волнуют размеры и я хочу сохранить изображения как есть, почему изменение размера принудительно, а ключи содержат размер изображения в ключах кеша? Не следует ли изменять размер как часть конфигурации или что-то в этом роде?
Для этого есть вариант DisplayImageOptions.imageScaleType(...)
.
Спасибо за ответ.
Я не думаю, что это используется редко. Я предложил решения.
Обычное :) В образце сетки есть этот баг. Список будет, если вам нужно сделать так, чтобы элемент не фиксировался по ширине или высоте.
Для этого есть опция DisplayImageOptions.imageScaleType (...).
Возможно, я ошибаюсь, но это повлияет только на растровое изображение, а не на ключи кеша. Мерцание по-прежнему будет происходить, а растровые изображения будут дублироваться. Если я хочу кэшировать исходные растровые изображения, почему ключи кеша различаются для разных представлений, какой смысл, если растровые изображения одинаковы?
Другой вариант использования:
у нас есть 2 представления, которые могут отличаться на несколько пикселей, поэтому теперь вы создадите 2 растровых изображения и потребляете вдвое больше памяти, чем это действительно необходимо.
Основная проблема в том, что глючная версия работает по умолчанию. И вам нужно проделать дополнительную работу, чтобы это исправить.
Вот почему так много приложений, использующих UIL, можно распознать с первой проверки: из-за мерцания при первой прокрутке.
У меня тоже возникла такая же проблема, пожалуйста, помогите мне. Вышеупомянутое решение не работает для меня.
Кто-нибудь?
Вышеупомянутое решение не работает для меня. Я применил обходной путь, поэтому решите проблему:
Я сохраняю загруженный URL-адрес в теге в просмотре изображений. Прежде чем начать загрузку с помощью загрузчика изображений, я проверяю, отображается ли уже в представлении изображения тот же URL-адрес.
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnFail(R.drawable.avatar).displayer(new RoundedBitmapDisplayer(100))
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
.cacheInMemory(true)
.cacheOnDisc(true).bitmapConfig(Bitmap.Config.RGB_565).build();
Я нашел не очень удачный способ решить эту проблему:
cacheInMemory(true)
Ребята, проблема все еще существует.
Откройте пример приложения -> GridView, дождитесь загрузки, прокрутите вниз, прокрутите вверх.
Могу подтвердить. Проблема все еще существует. использование действия с listivew и запуск нового действия. возврат к действию со списком -> изображения мерцают.
Теперь я использую Picasso, который очень хорошо справляется с этим :)
Привет!
Я исправил эту проблему с помощью вспомогательного класса (очистить анимацию ImageView
и вручную установить Bitmap
из кеша памяти):
protected static void loadImageByPath(DisplayImageOptions options, String path, ImageViewAware image, ImageLoadingListener listener) {
ImageLoader.getInstance().cancelDisplayTask(image);
image.getWrappedView().clearAnimation();
image.setImageDrawable(null);
final ImageSize size = ImageSizeUtils.defineTargetSizeForView(image, null);
final String cacheKey = MemoryCacheUtil.generateKey(path, size);
final List<Bitmap> cachedBitmaps = MemoryCacheUtil.findCachedBitmapsForImageUri(cacheKey, ImageLoader.getInstance().getMemoryCache());
if (cachedBitmaps.size() > 0) {
final Bitmap bitmap = cachedBitmaps.get(0);
// Yep, sometimes it is null
if (bitmap != null) {
if (listener != null) {
listener.onLoadingComplete(path, image.getWrappedView(), bitmap);
}
image.setImageBitmap(bitmap);
return;
}
}
ImageLoader.getInstance().displayImage(path, image, options, listener);
}
Моя стратегия загрузки:
ImageView
перед загрузкой ( resetViewBeforeLoading(true)
);Bitmap
для элемента в кеше памяти - он будет установлен без затухания;Bitmap
для элемента в кеше памяти - он будет загружен и установлен в ImageView
с исчезновением.DisplayImageOptions
:final DisplayImageOptions fadeDisplayOptionsForList = new DisplayImageOptions.Builder()
.resetViewBeforeLoading(true)
.showImageOnLoading(R.color.color_image_loading)
.showImageForEmptyUri(R.drawable.image_catalog_error)
.showImageOnFail(R.drawable.image_catalog_error)
.cacheInMemory(true)
.cacheOnDisc(true)
.postProcessor(null)
.imageScaleType(ImageScaleType.EXACTLY)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(200))
.build();
Я использую FadeInBitmapDisplayer
в моем DisplayImageOptions
и думаю, что ImageLoader
не очищает Animation
после установки Bitmap
. Когда вы вызываете Adapter.notifyDataSetChanged()
, AdapterView
вызывает View.requestLayout()
что делает его потомков недействительным и перезапускает Animation
.
Ребята !!! Для галереи с изображениями переменной высоты ....
На самом деле вы должны ПОЛУЧИТЬ ШИРИНУ И ВЫСОТУ С СЕРВЕРА. Действительно, вы должны хранить их на сервере, когда сохраняете изображения!
Затем используйте соотношение и установите абсолютный размер. ЗАТЕМ загрузите изображение с помощью великолепного AUIL.
@carlonzo Как вы решили проблему, я пробовал все упомянутое, все еще мерцает.
Это мой код отображаемого изображения.
public void displayImage(Drawable fallback, ImageView imageView,
String imageURL) {
ImageLoader imageLoader = ImageLoader.getInstance();
ImageViewAware aware = new ImageViewAware(imageView, false);
ImageLoader.getInstance().cancelDisplayTask(aware);
aware.getWrappedView().clearAnimation();
aware.setImageDrawable(null);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.resetViewBeforeLoading(true).cacheOnDisk(true)
.postProcessor(null).delayBeforeLoading(0).cacheInMemory(true)
.showImageForEmptyUri(fallback).showImageOnFail(fallback)
.showImageOnLoading(fallback)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(150))
.imageScaleType(ImageScaleType.EXACTLY).build();
final ImageSize size = ImageSizeUtils.defineTargetSizeForView(aware,
new ImageSize(480, 800));
final String cacheKey = MemoryCacheUtils.generateKey(imageURL, size);
List<Bitmap> bitMaps = MemoryCacheUtils.findCachedBitmapsForImageUri(
cacheKey, imageLoader.getMemoryCache());
int length = bitMaps.size();
if (bitMaps != null && length > 0) {
if (bitMaps != null && length > 0 && bitMaps.get(0) != null) {
if (lister != null) {
lister.onLoadingComplete(imageURL, aware.getWrappedView(),
bitMaps.get(0));
}
imageView.setImageBitmap(bitMaps.get(0));
} else {
imageLoader.displayImage(imageURL, aware, options);
}
} else {
imageLoader.displayImage(imageURL, aware, options);
}
}
@nfirex Я пробовал тот же код, что и ваш, не работает с прокручиваемыми изображениями flickr
Пожалуйста помоги
Это исправление, чтобы остановить мерцание;)
<strong i="6">@Override</strong>
public View getView(int position, View convertView, ViewGroup parent) {
...
// Set image
String imgUrl = getItem(position);
String tag = null;
Object obj = holder.image.getTag();
if (obj != null) {
tag = obj.toString();
}
// Check if the same url
if (!TextUtils.equals(imgUrl, tag)) {
mImageLoader.cancelDisplayTask(holder.image);
mImageLoader.displayImage(imgUrl, holder.image, mDiOptions);
holder.image.setTag(imgUrl);
}
...
}
Похоже, эта проблема все еще существует с последней версией Android Lollipop. Я использую UIL v1.9.3, и мерцание изображения происходит только на устройствах Lollipop.
Я решил эту «ошибку» (это определенно не так) с помощью гораздо более простого подхода - нет необходимости в специальных помощниках или решениях, просто правильно используйте тег View и шаблон ViewHolder:
<strong i="6">@Override</strong>
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.row_layout, parent, false);
viewHolder = new ViewHolder();
viewHolder.title = (TextView) convertView.findViewById(R.id.row_layout_title);
viewHolder.image = (ImageView) convertView.findViewById(R.id.row_layout_imgView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
Item item = someAdapterArray.get(position);
viewHolder.title.setText(item.getTitle());
//lets prevent "blinking" by "saving URL tag" hack
if (viewHolder.image.getTag() == null ||
!viewHolder.image.getTag().equals(item.getImageUrl())) {
//we only load image if prev. URL and current URL do not match, or tag is null
ImageLoader.getInstance().displayImage(item.getImageUrl(), viewHolder.image, new SimpleImageLoadingListener());
viewHolder.image.setTag(item.getImageUrl());
}
return convertView;
}
Да, это много тегов, но это предотвратит перезагрузку ваших ImageViews при любых незначительных изменениях.
в состояние ListView / GridView.
PS Возьмите событие открытия / закрытия NavigationDrawer в качестве примера такого «тонкого изменения» - за ним будет следовать notifyDataSetChanged()
даже если не произошло никаких изменений в базовых данных адаптера.
Спасибо ...
ImageAware imageAware = новый ImageViewAware (imageView, ложь)
imageLoader.displayImage (imageUri, imageAware);
Спасибо за работу для меня
ребята. Эта библиотека была отличной. но теперь есть более развитые библиотеки. пожалуйста, проверьте Picasso, Glide или Fresco.
Есть ли исправление или решение? Я пробовал предыдущие решения, такие как установка тега ImageView.
Та же проблема возникает при использовании RecyclerView & notifidatasetchanged ();
@ nostra13
РЕДАКТИРОВАТЬ:
Это моя вина, вместо этого я должен был позвонить в notificationdatainsert, тогда все заработало!
спасибо @ousenko
спасибо @ousenko отлично работает)
в recyclerView попробуйте использовать: notifyItemChanged (position);
в моем коде работает.
ИЛИ ЖЕ:
попробуйте получить растровое изображение по методу:
частный статический Bitmap getCachedBitmap (String url) {
Список
если (b! = null && b.size ()> 0)
возврат b.get (0);
return null;
}
затем в коде:
Растровое изображение cachedThumbnail = getCachedBitmap (url);
if (cachedThumbnail! = null) {
image.setImageBitmap (cachedThumbnail);
возвращаться;
}
ImageLoader.getInstance (). DisplayImage (...);
Привет, друг,
Эта проблема исправлена?
Привет @SagarPanwala, я использую v1.9.5 "ошибка" все еще существует, вы можете использовать упомянутый обходной путь ousenko
спасибо @ousenko , это отличный ответ, может я
Может ли кто-нибудь сказать мне, как использовать это в ViewPager, который используется как элемент ListView? У меня проблемы с этим.
То же самое для меня.
и я думаю, что важно проверить конфигурацию для параметров инициализации.
Спасибо за ваше решение. лол :)) @ousenko
Спасибо @ousenko
Самый полезный комментарий
Я решил эту «ошибку» (это определенно не так) с помощью гораздо более простого подхода - нет необходимости в специальных помощниках или решениях, просто правильно используйте тег View и шаблон ViewHolder:
Да, это много тегов, но это предотвратит перезагрузку ваших ImageViews при любых незначительных изменениях.
в состояние ListView / GridView.
PS Возьмите событие открытия / закрытия NavigationDrawer в качестве примера такого «тонкого изменения» - за ним будет следовать
notifyDataSetChanged()
даже если не произошло никаких изменений в базовых данных адаптера.