English version - "Universal Image Loader. Part 2 - Configuration"
Предыдущая статья: "Universal Image Loader. Part 1 - Введение"
Все манипуляции проходят посредством класса ImageLoader. Это singletone, поэтому чтобы получить единственный экземпляр класса надо вызвать метод getInstance(). Перед использованием ImageLoader'а по назначению (для отображения картинок), необходимо проинициализировать его конфигурацией - ImageLoaderConfiguration, с помощью метода init(...). Ну а дальше можно со спокойной душой юзать все вариации метода displayImage(...).
В общем, самый простой вариант использования ImageLoader'a (с конфигурацией по умолчанию) представлен ниже:
А теперь рассмотрим полный функционал.
Как вы уже поняли, сначала ImageLoader надо проинициализировать с помощью объекта конфигурации. Т.к. ImageLoader - синглтон, то инициализировать его нужно один раз за запуск приложения. Я бы рекомендовал делать это в перегруженном Application.onCreate(). Повторная инициализация уже проинициализированного ImageLoader'а не будет иметь никакого эффекта.
Значит, создаем конфигурацию, а это объект класса ImageLoaderConfiguration. Создаем с помощью Builder'а:
Итак, конфигурация создана. Теперь ею можно проинициализировать ImageLoader:
Следующая статья: "Universal Image Loader. Part 3 - Usage"
Предыдущая статья: "Universal Image Loader. Part 1 - Введение"
Все манипуляции проходят посредством класса ImageLoader. Это singletone, поэтому чтобы получить единственный экземпляр класса надо вызвать метод getInstance(). Перед использованием ImageLoader'а по назначению (для отображения картинок), необходимо проинициализировать его конфигурацией - ImageLoaderConfiguration, с помощью метода init(...). Ну а дальше можно со спокойной душой юзать все вариации метода displayImage(...).
В общем, самый простой вариант использования ImageLoader'a (с конфигурацией по умолчанию) представлен ниже:
ImageView imageView = ... // вьюха, где будет отображать картинку String imageUrl = ... // URL картинки (н-р: "http://site.com/image.png", "file:///mnt/sdcard/img/image.jpg") ImageLoader imageLoader = ImageLoader.getInstance(); // Получили экземпляр imageLoader.init(ImageLoaderConfiguration.createDefault(context)); // Проинициализировали конфигом по умолчанию imageLoader.displayImage(imageUrl, imageView); // Запустили асинхронный показ картинки
А теперь рассмотрим полный функционал.
Как вы уже поняли, сначала ImageLoader надо проинициализировать с помощью объекта конфигурации. Т.к. ImageLoader - синглтон, то инициализировать его нужно один раз за запуск приложения. Я бы рекомендовал делать это в перегруженном Application.onCreate(). Повторная инициализация уже проинициализированного ImageLoader'а не будет иметь никакого эффекта.
Значит, создаем конфигурацию, а это объект класса ImageLoaderConfiguration. Создаем с помощью Builder'а:
File cacheDir = StorageUtils.getCacheDirectory(context, "UniversalImageLoader/Cache"); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .memoryCacheExtraOptions(480, 800) // width, height .discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75) // width, height, compress format, quality .threadPoolSize(5) .threadPriority(Thread.MIN_PRIORITY + 2) .denyCacheImageMultipleSizesInMemory() .memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024)) // 2 Mb .discCache(new UnlimitedDiscCache(cacheDir)) .discCacheFileNameGenerator(new HashCodeFileNameGenerator()) .imageDownloader(new BaseImageDownloader(5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s) .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) .enableLogging() .build();Рассмотрим каждую настройку:
- memoryCacheExtraOptions() используется при декодировании изображений в объекты Bitmap. Чтобы не хранить в памяти полноразмерную картинку, она уменьшается до размера, определяемых из значений параметров ImageView, куда грузится картинка: maxWidth и maxHeight (в первую очередь), layout_width и layout_height (во вторую очередь). Если эти параметры не определены (значения fill_parent и wrap_content считаются неопределенными), то тогда берутся размеры, заданные параметрами из данного memoryCacheExtraOptions() ;
- Значения по умолчанию - размеры экрана аппарата
- discCacheExtraOptions() задает параметры для хранения картинок на "дисковом кэше". По умолчанию на дисковом кэше хранятся полноразмерные картинки. С помощью данной настройки можно задать максимальный размер для хранимых картинок, а также формат сжатия и качество (для JPEG).
- threadPoolSize(int) задает размер пула потоков. Каждое задание на загрузку и отображение картинки выполняется в отдельном потоке, а те потоки, в которых происходит загрузка изображений из сети, попадают в пул. Т.о. размер пула определяет количество потоков, работающих одновременно. Задание большого размера пула может существенно снизить скорость работы UI, например в списке может подтормаживать прокрутка.
- Значения по умолчанию - 5
- threadPriority(int) задает приоритет в системе (от 1 до 10) всех потоков, в которых выполняются задачи;
- Значения по умолчанию - 4
- вызов denyCacheImageMultipleSizesInMemory() накладывает запрет на хранение в памяти разных размеров одной и той же картинки. Т.к в дисковом кэше хранятся полноразмерные картинки, а при загрузке в память они уменьшаются до размеров ImageView, в которых они должны отобразиться, то бывают случаи, что одну и ту же картинку необходимо отобразить сначала в маленькой вьюшке, а потом в большой. При этом в памяти будут хранится две Bitmap'ки разных размеров, представляющие одинаковую картинку. Это поведение по умолчанию. Инструкция denyCacheImageMultipleSizesInMemory() обеспечивает удаление из кэша в памяти предыдущего размера загружаемой картинки.
- с помощью memoryCache(...) можно задать реализацию кэша в памяти. Можно воспользоваться готовыми решениями (все они представляют собой реализации кэша ограниченного размера, где при превышении размера кэша из него удаляется объект по определенному алгоритму):
- FIFOLimitedCache (удаляется объект по принципу First-In-First-Out)
- LargestLimitedCache (удаляется самый большой по размеру объект)
- UsingAgeLimitedCache (удаляется объект с самой давней датой доступа)
- UsingFreqLimitedCache (удаляется самый редко используемый объект)
- Значение по умолчанию - UsingFreqLimitedCache с ограничением памяти в 2 Мб
- memoryCacheSize(int) задает максимальный размер кэша в памяти. В этом случае используется кэш по умолчанию - UsingFreqLimitedCache.
- Значение по умолчанию - 2 Мб
- с помощью discCache(...) можно задать реализацию кэша на файловой системе. Можно воспользоваться готовыми решениями (в них файлы, соответствующие определенным URL-ам, именуются как хеш-коды этих URL-ов):
- UnlimitedDiscCache (обычный кэш, без ограничений)
- FileCountLimitedDiscCache (ограниченный по количеству файлов кэш)
- TotalSizeLimitedDiscCache (ограниченный по размеру кэш)
- Значение по умолчанию - UnlimitedDiscCache
- discCacheSize(int) задает максимальный размер кэша на файловой системе. В этом случае используется TotalSizeLimitedDiscCache.
- discCacheFileCount(int) задает максимальное количество файлов в дисковом кэше. В этом случае используется FileCountLimitedDiscCache.
- с помощью discCacheFileNameGenerator(...) можно задать свой генератор имен для файлов закешированных на диске картинок: на входе получается URI картинки, на выходе надо выдать уникальное имя для файла (FileNameGenerator).
Есть готовые варианты:- HashCodeFileNameGenerator - в качестве имени файла берется java хеш-код URI картинки
- Md5FileNameGenerator - в качестве имени файла генерируется хеш-код по алгоритму MD5
- с помощью imageDownloader(...) можно задать свою реализацию загрузчика картинок: нужно представить свою имплементацию интерфейса ImageDownloader.
Можно воспользоваться готовыми реализациями (по надобности их также можно унаследовать):- BaseImageDownloader - используется URLConnection для работы с сетью
- HttpClientImageDownloader - используется HttpClient для работы с сетью
- с помощью defaultDisplayImageOptions(...) можно задать опции отображения картинок, которые будут использоваться для всех вызовов метода displayImage(...), где не были переданы кастомные опции. Подробнее, что это за эти опции, будет рассказано ниже.
- enableLogging() включает подробное логгирование процесса работы ImageLoader'a.
ImageLoaderConfiguration config = ImageLoaderConfiguration.createDefault(context);
Итак, конфигурация создана. Теперь ею можно проинициализировать ImageLoader:
ImageLoader.getInstance().init(config);Вот и все, ImageLoader готов к использованию. Но об этом Я расскажу уже в следующей статье.
Следующая статья: "Universal Image Loader. Part 3 - Usage"
Не могу понять почему происходит именно так. Возможно это известная ситуация. Используестя андроид 2.1. Вот кусок лога
ReplyDelete08-19 20:46:42.249: I/ImageLoader(469): Load image from Internet [http://img...jpg_48x80]
08-19 20:46:42.500: I/global(469): Default buffer size used in BufferedInputStream constructor. It would be better to be explicit if an 8k buffer is required.
На 4.1 картинки показываются. Возможно я что-то пропустил и есть минимальные требования к SDK?
Минимальный API Level - 1.5. Не понял, в чем проблема? Не отображаются картинки на 2.1? В приведенных логах никаких ошибок не вижу. Или это все, что есть в логах?
DeleteДа именно так на android 2.1 картинки не показываются.
DeleteВот лог http://codepaste.ru/11565/
Предпологаю что причина в фабрике SkImageDecoder::Factory returned null
Начиная с android 2.2 картинки отображаются.
А пример проекта с GitHub (который UniversalImageLoaderExample) у вас работает на 2.1? Показывает картинки?
DeleteСкажите а можно добавить в loader свой httpclient? Например, если в приложении уже используется кастомный httpclient как синглтон, чтоб уже можно было его передать в ваш лоадер.
ReplyDeleteДа, можно. Надо в конфигурацию передать свой imageDownloader. Можно воспользоваться HttpClientImageDownloader, который в конструктор принимает HttpClient.
DeleteПодскажите пожалуйста, можно ли указать путь к картинке, которая находится в assets ?
ReplyDeleteт.к. при file:///android_asset/roks.png
выдает ошибку
/android_asset/roks.png (No such file or directory)
хотя файл присутствует. при изменении на file:///mnt/sdcard/ все работает прекрасно
в чем может быть ошибка ?
Путь типа file://... указывает на файловую систему девайса. Assets папка лежит в приложении и не может быть доступна, как обычная папка на файловой системе.
DeleteТут есть два выхода:
- в начале скопировать все файлы из assets на SD карту, а потом доступаться к ним через file://...
- создать свой ImageDownloader (и засетить его в конфигурации), перегрузить InputStream getStreamFromOtherSource(URI imageUri), в нем по URI найти нужный вам файл в assets и перегнать его в InputStream. Для картинок из assets вызывать displayImage(...) c URI типа этого: "assets://roks.png".
Скоро примерчик для второго случая включу в Example проект на GitHub.
Включил.
DeleteПочему может не сохранять картинки в папке для кеша? пермишн стоит. папка существует (перепроверено много раз). Вот мои конфиги
ReplyDeleteImageLoaderConfiguration imageLoaderconfig = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(50, 50) // width, height
.discCacheExtraOptions(50, 50, Bitmap.CompressFormat.PNG, 75)
.threadPoolSize(3)
.threadPriority(Thread.NORM_PRIORITY - 2)
.denyCacheImageMultipleSizesInMemory()
.offOutOfMemoryHandling()
.memoryCache(new UsingFreqLimitedMemoryCache(5 * 1024 * 1024)) // 2 Mb
.discCacheSize(15000)
.discCacheFileCount(50)
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.enableLogging()
.build();
Readme читали? https://github.com/nostra13/Android-Universal-Image-Loader#useful-info
DeleteКэшировать или не кэшировать картинку выставляется в DisplayImageOptions. В простых опциях - DisplayImageOptions.createSimple() - кэширование ни в памяти, ни на диске не включено.
Также вызовы в вашей конфигурации:
.discCacheSize(15000)
.discCacheFileCount(50)
.discCache(new UnlimitedDiscCache(cacheDir))
перекрывают друг друга. Информация об этом есть в Java доках, и в логах выводятся варнинги. Выбирайте что-то одно.
Ой ёй, извините что так сильно натупил.. Ушел читать доки.
DeleteПривет, спасибо за библиотечку.
ReplyDeleteЕсть один вопрос. У меня почему-то фотки (которые я снимаю и складываю во внутреннее хранилище) отображаются в неправильной ориентации. Они всегда повернуты на 90 градусов.
Вы с этим не сталкивались? Может подскажете как это исправить?
Спасибо.
Привет. Скорее всего это из-за того, что поворот хранится в EXIF метаданных, а UIL никак не учитывает EXIF при отображении (пока). Вы можете создать свой BitmapDisplayer, где будете учитывать поворот, и засетить его в конфигурацию.
DeleteХотя не, тут нужен доступ к файлу картинки. Тогда меняйте исходники или ждите, пока не добавится эта фича. Сколько ждать - не знаю.
DeleteСпасибо Сергей.
ReplyDeleteпри быстрой прокрутки, картинки сбрасываються на дефолтные, как оставить картинки на своих местах ?
ReplyDeleteПодскажите пожалуйста есть ли возможность установить listener на загрузку ряда картинок? Чтоб, к примеру, ProgressDialog скрывался только после загрузки всей очереди , а не после каждой картинки
ReplyDeleteЕсли только вы сами будете отслеживать это в листенере. Счетчик там какой-нибудь заведете или типа того, а листенере проверять, если счетчик равен кол-ву картинок - скрывайте ProgressDialog.
DeleteКэширую изображению вот сюда: cacheDir = StorageUtils.getCacheDirectory(Context)
ReplyDeleteкак из этого каталога определенно получить изображение?
Там они все храняться с непонятными именами..
File image = DiscCacheUtil.findInCache(imageUri, imageLoader.getDiscCache());
DeleteСсылка на изображение содержит кириллицу.
ReplyDeleteВ списке все отображается нормально. В ImageLoaderConfiguration указал memoryCache. Когда пытаюсь найти путь к изображению в кэшэ, использую этот метод
DiscCacheUtil.findInCache(logoUrl.get(position), ImageLoader.getInstance().getDiscCache())
он возвращает null.
В чем может быть проблема?
Читайте Readme - Useful Info, где и как включается кэш.
DeleteВ конфигурации вы лишь настраиваете кэши. А уж кэшировать или нет - это в DisplayImageOptions. И не забывайте разницу между кэшированием в памяти и на диске.
В DisplayImageOptions указал cacheInMemory()
DeleteИ все?
DeleteНет, вот все, что указал:
Deleteoptions = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.showImageForEmptyUri(R.drawable.image_for_empty_url)
.cacheInMemory()
.cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2).build();
Что-то так и не понял, в чем косяк... Может быть так, что это проблема заключается в том, что путь к картинке содержит кириллицу?
DeleteКонфигурация какая у вас?
DeleteFile cacheDir = StorageUtils.getCacheDirectory(EngineBrowseByOnline.this);
DeleteImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.memoryCacheExtraOptions(100, 100)
.threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 2)
.offOutOfMemoryHandling()
.memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024))
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.discCacheFileCount(100)
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()).enableLogging()
.build();
Да, это проблема связана с кириллицей. Пофикшу в следующей версии.
DeleteПока решение для вас такое:
String encodedUrl = Uri.encode(logoUrl.get(position), "@#&=*+-_.,:!?()/~'%");
DiscCacheUtil.findInCache(encodedUrl, ImageLoader.getInstance().getDiscCache());
Спасибо!
DeleteЖду следующей версии..
Сергей, спасибо! Отличная библиотека! Подскажите, а если мне не нужно сохранять картинки в кеше. Как правильно настроить объект?
ReplyDeleteГде не нужно кэшировать? В памяти или на файловой системе?
DeleteВ DisplaytImageOptions есть .cacheInMemory() и .cacheOnDisc(). Если эти опции не включать, то и кэшироваться не будет.
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ImagePagerActivity.this).build();
DeleteimageLoader.init(config);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.resetViewBeforeLoading().cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(300))
.build();
Я рисую изображение, показываю его пользователю с помощью DisplayImageOptions. После я рисую уже новое изображение, но сохраняю его с тем же именем и в тоге получаю старую картинку.
Все, понял свою ошибку. Надо было убрать .resetViewBeforeLoading().cacheOnDisc().
DeleteСергей, привет. За библиотеку спасибо! Подскажи, пожалуйста, столкнулся с проблемой. Если изображение уже есть в кэше и по этому же url подсунуть другое изображение, то оно не загрузится, а отобразится старое из кэша.
ReplyDeleteВот, например: Есть файл site.ru/image/sample.jpg на котором изображена буква А, это изображение загрузили и сохранили в кэш. Потом по этому же адресу на веб сервере положили изображение с буквой Б (т.е. содержимое изменилось, а адрес нет). Так вот UIL не загружает новый файл, потому что url не изменился и показывает старое изображение из кэша. Как такое решить?
Надо удалить сперва изображения из кэшей. Посмотри в сторону MemoryCacheUtil и DiscCacheUtil. Есть соответствующие имплементации кэшей (LimitedAge...), которые будут удалять закэшированные картинки через указанное время, тем самым форсируя их повторную загрузку с сервера.
DeleteLimitedAge.. видимо в более поздних версиях библиотеки появилось, у меня версия 1.5.2 :) Буду обновляться, спасибо
DeleteЗдравствуйте!
ReplyDeleteПодскажите, в чем может быть проблема.
Сделал gridview с загрузкой изображений. url лежат в txt файле.
Все работает, но при прокрутке экрана изображения меняются, на место старого ставится новое.
Код:
public class MainActivity extends Activity {
ImageLoader imageLoader;
GridView gv;
ArrayList imageUrls = getUrlList();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(getApplicationContext()));
gv = (GridView) findViewById(R.id.gridView);
gv.setAdapter(new ImageAdapter());
public class ImageAdapter extends BaseAdapter {
@Override
public int getCount() {
return imageUrls.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView imageView;
if (convertView == null) {
imageView = (ImageView) getLayoutInflater().inflate(R.layout.grid_image, parent, false);
} else {
imageView = (ImageView) convertView;
}
imageLoader.displayImage(imageUrls.get(position), imageView);
return imageView;
}
}
public ArrayList getUrlList() {
//Метод для считывания url изображений в ArrayList.
}
}
В DisplayImageOptions надо включить .resetViewBeforeLoading().
DeleteFileCountLimitedDiscCache (ограниченный по размеру кэш)
ReplyDeleteTotalSizeLimitedDiscCache (ограниченный по количеству файлов кэш)
я так понимаю здесь перепутаны местами описания
This comment has been removed by the author.
ReplyDeleteНичем не могу помочь.
DeleteОй, да я удалил уже коммент) У меня другой вопрос, как сделать принудительный кеш на сдкарту? Сейчас кеширует куда-то, но не понимаю куда, посмотреть только можно в свойствах приложения. А надо бы, чтобы кешировало в отдельную папку на диске.
DeleteimageLoader = ImageLoader.getInstance(); // Получили экземпляр
File cacheDir = StorageUtils.getOwnCacheDirectory(this, "UniversalImageLoader/Cache");
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
//.memoryCacheExtraOptions(480, 800) // width, height
//.discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null) // width, height, compress format, quality
.threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 2)
.denyCacheImageMultipleSizesInMemory()
//.memoryCache(new UsingFreqLimitedMemoryCache(4 * 1024 * 1024)) // 2 Mb
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.imageDownloader(new BaseImageDownloader(this, 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.enableLogging()
.build();
imageLoader.init(config);
Папки не создается, может я что-то делаю не так?
У вас разрешение на запись на карточку прописано?
DeleteЕсли имеется ввиду
DeleteТо да, прописано. Папка создается, но туда ничего не помещается. Может что-то стоит отключить в ImageLoaderConfiguration?
Спасибо.
android.permission.WRITE_EXTERNAL_STORAGE
DeleteЗначит вы наверное прочто не включаете кэширование. Его надо включать в display options.
DeleteПодскажите пожалуйста как это сделать? То, что я нашел DisplayImageOptions, там включил cacheOnDisc(), но мне выдает "The method cacheOnDisc() from the type DisplayImageOptions.Builder is deprecated". Что делать? И если я создам DisplayImageOptions, то мне всегда его надо добавлять при загрузке каждой картинки?
Deleteвроде разобрался, поставил в DisplayImageOptions - cacheOnDisc(true), теперь кеширует на диск. Буду проверять как работает)
DeleteЕсли в проекте надо грузить картинки с разными конфигами - в случае вашего синглтона это никак?
ReplyDeleteА чем конфиги отличаться будут?
DeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteКогда вызывается ImageLoadingListener.onLoadingCancelled(String, View)? (я использую ViewPager, и иногда, при одновременной загрузке нескольких картинок, одна из них не не загружается, а вызывается onLoadingCancelled)
ReplyDeleteImageLoadingListener.onLoadingCancelled(...) вызывается когда ImageView была переиспользована для загрузки новой картинки.
Deleteкак этого можно избежать? я столкнулся с подобной ошибкой..
DeleteЭто не ошибка. Или у вас картинки не отображаются?
DeleteЗдравствуйте! крутая штука, спасибо. Столкнулся с проблемой: если в системных настройках почистить кэш, то лоадер не находит папку cach, в логи пишет FileNotFoundException. Хотелось бы использовать именно эту папку для возможности чистки кэша из настроек. Есть ли решение кроме как изменять исходники?
ReplyDeleteЗдравствуйте. Я уже не помню точно, но папка вроде должна пересоздаваться, если ее вдруг удалили.
DeleteСоздается она при старте приложения (при инициализации лоадера), а если удалить ее при запущенном приложении то в логи сыпет ексепшены а вместо фоток - черный экран. Буду искать решение, спасибо за ваши труды.
DeleteПожалуйста.
DeleteКакая версия библиотеки? Логика по воссоздания папки кэша точно есть: LoadAndDisplayImageTask.getImageFileInDiscCache()
Туплю - либа была 1.6.1 - в onResume() сам создавал папку cach. Скачал 1.8.6 - все решилось. Спасибо за терпение)
DeleteЗдравствуйте, в одном из первых постов Вы написали, что добавили в пример как работать с assets. Если не трудно укажите пожалуйста где этот пример конкретно лежит, я не могу найти указанный Вами тут
ReplyDeletehttps://github.com/nostra13/Android-Universal-Image-Loader/issues/123 - ExtendedImageDownloader
Спасибо
Здравствуйте. Теперь UIL поддерживает загрузку из assets из коробки. Просто передавайте Uri типа "assets://..."
DeleteСпасибо большое
DeleteЗдравствуйте, спасибо за библиотеку. Можно ли ее настроить чтобы также показывать thumbnails видео файлов? Вот вопрос на stackoverflow:
ReplyDeletehttp://stackoverflow.com/questions/20931585/is-it-possible-to-display-video-thumbnails-using-universal-image-loader-and-how
Спасибо!
Уже сделал. Я сделал для этого новый ImageDecoder, ответ можно посмотреть в том же линке.
DeleteЗдравствуйте Сергей, спасибо за библиотеку. Как загрузить картины с хорошим качеством ?
DeleteЗдравствуйте. Они загружаются в дисковый кэш в полном качестве, в кэш в памяти ресайзятся для экономии памяти. Чтобы не ресайзились используйте DisplayImageOptions.imageScaleType(ImageScaleType.NONE).
DeleteПопробовал поставить , но качество тоже самое
ReplyDeleteDisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options).imageScaleType(ImageScaleType.NONE).build(); (LoadAndDisplayImageTask класс) и .cacheOnDisc().imageScaleType(ImageScaleType.NONE) (ImagePagerActivitu класс)
Пожалуйста скажите где поставить эго
DeleteЗдравствуйте Сергей , очень приятно что есть такие программисты как вы , я хотель получать фотографии с очень хорошим качеством , как мне это сделать попробовал DisplayImageOptions.imageScaleType(ImageScaleType.NONE) но все тоже самое , как мне быть где конкретно это поставить , может быть на еще что то поменять .Заранее Спасибо
ReplyDeleteЗдравствуйте, а что "то же самое"? Почему вы думаете, что показываются картинки не в полном качестве? Покажите свой конфиг, опции, сам код вызова imageLoader.
Deleteprivate boolean downloadSizedImage(File targetFile, int maxWidth, int maxHeight) throws IOException {
Delete// Download, decode, compress and save image
ImageSize targetImageSize = new ImageSize(maxWidth, maxHeight);
DisplayImageOptions specialOptions = new DisplayImageOptions.Builder().cloneFrom(options).imageScaleType(ImageScaleType.NONE).build(); //// Здесь
Это в LoadAndDisplayImageTask и
options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error).resetViewBeforeLoading()
.cacheOnDisc().imageScaleType(ImageScaleType.NONE) //// Здесь
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(300)).build(); а это в ImagePagerActivity
Почему вы думаете, что показываются картинки не в полном качестве?
DeleteЯ точно не уверен , а ImageScaleType.NONE поставлены в правильных местах ?
DeleteИсходники менять не нужно.
DeleteЯ так и не увидел вашей конфигурации и кода вызова displayImage().
Сергей Здравствуйте , спасибо за библиотеку , я отключил кэш .cacheInMemory(false) , .cacheOnDisc(false) в DisplayImageOptions но кэш заполняется в чем проблема ?
ReplyDeleteЗдравствуйте, Сергей!
ReplyDeleteЕсть ли возможность использовать в качестве имени не MD5 от URL, а MD5 от самой картинки, от ByteArray, скажем.
Здравствуйте. Есть возможность, но это будет неэффективно, т.к. вы должны будете делать дополнительный запрос. А вообще за имена отвечает FileNameGenerator, который можно передать в конфигурацию..
DeleteВ том то и дело, что у меня есть URL и Hash, в случае, если Hash поменялся, мне необходимо загрузить новую картинку. Посему я бы хотел переложить эту работу на Ваш Loader, т.е давать ему хеш, который я получаю от сервера и URL, если хеш той картинки, что я получаю от сервера не совпадает с тем что у меня есть, то я по тому же самому URL загружаю новую. Расскажите, пожалуйста, какая есть для этого возможность.
DeleteFileNameGenerator я смотрел с самого началаю Иначе бы не писал, т.к. в качестве параметра используется URI, как мне использовать hash от самой картинки в качестве имени, подскажите, пожалуйста?
DeleteНет такой возможности. Когда в начале мы проверяем наличие картинки в кэше на диске - у нас есть только URI. По нему мы генерим имя файла и потом ищем этот файл в кэше, чтобы понять, нужно ли загружать картинку из сети. Чтобы генерировать имена исходя их самой картинки - ее по-любому надо загрузить сначала, т.е. смысл дискового кэша теряется. Можете его просто отключить тогда.
DeleteДобрый День, Сергей! Спасибо за библиотеку!
ReplyDeleteУ меня следующая задача, на сайте изображения хранятся по папкам, предполагаемый уровень вложенности не будет превышать 2, то есть есть каталоги по датам, а в них определенные темы. Выкачивая эти изображения хотелось бы повторить структуру каталогов и на устройстве для дальнейшей обработки как даты, так и названия тем в приложении. Подскажите, пожалуйста, наилучший способ этого добиться, если это возможно, а если нет, то можно ли что-то предпринять для решения этой задачи? Заранее спасибо за ответ!
Добрый. Какую имплементацию дискового кэша вы используете? Если дефолтную (UnlimitedDisckCache), то можете унаследоваться от нее, перегрузить метод [File get(String imageUri)] и там исходя из URL возвращаться файл в нужной папке (файл не обязательно должен существовать, этот метод вызывается когда UIL хочет получить объект файла для такого-то URL, и если тот не существует, то этот файл создается).
DeleteТ.е. метод может выглядет примерно так:
@Override
public File get(String imageUri) {
String fileName = fileNameGenerator.generate(imageUri);
File dir = cacheDir;
... // Определяем из URL нужно ли создавать подпапку
dir = new File(dir, "subdir"); // Если да, создаем подпапку
if (!dir.exists()) {
dir.mkdirs();
}
return new File(dir, fileName);
}
Свой кэш потом надо засетить в конфигурацию.
Да, это то, что нужно было! Спасибо!
DeleteЕще 1 уточняющий вопрос, какая реакция при достижении установленного размера дискового кэша? То есть он очищается полностью или только его часть?
Удаляется файлы, которые использовались максимально давно. Удаляются, пока общий размер кэша не вложится в лимит.
Delete11-30 19:46:30.608: E/ImageLoader(22224): URI: content://com.android.contacts/contacts/1326/photo/photo, calling user: com.mylauncher, calling package:com.mylauncher
ReplyDelete11-30 19:46:30.608: E/ImageLoader(22224): java.lang.IllegalArgumentException: URI: content://com.android.contacts/contacts/1326/photo/photo, calling user: com.mylauncher, calling package:com.mylauncher
11-30 19:46:30.608: E/ImageLoader(22224): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:167)
11-30 19:46:30.608: E/ImageLoader(22224): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
11-30 19:46:30.608: E/ImageLoader(22224): at android.content.ContentProviderProxy.query(ContentProviderNative.java:366)
11-30 19:46:30.608: E/ImageLoader(22224): at android.content.ContentResolver.query(ContentResolver.java:370)
11-30 19:46:30.608: E/ImageLoader(22224): at android.content.ContentResolver.query(ContentResolver.java:313)
11-30 19:46:30.608: E/ImageLoader(22224): at android.provider.ContactsContract$Contacts.openContactPhotoInputStream(ContactsContract.java:1985)
11-30 19:46:30.608: E/ImageLoader(22224): at android.provider.ContactsContract$Contacts.openContactPhotoInputStream(ContactsContract.java:2016)
11-30 19:46:30.608: E/ImageLoader(22224): at com.nostra13.universalimageloader.core.download.BaseImageDownloader.getStreamFromContent(BaseImageDownloader.java:187)
11-30 19:46:30.608: E/ImageLoader(22224): at com.nostra13.universalimageloader.core.download.BaseImageDownloader.getStream(BaseImageDownloader.java:90)
11-30 19:46:30.608: E/ImageLoader(22224): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.downloadImage(LoadAndDisplayImageTask.java:290)
11-30 19:46:30.608: E/ImageLoader(22224): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.tryCacheImageOnDisk(LoadAndDisplayImageTask.java:273)
11-30 19:46:30.608: E/ImageLoader(22224): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.tryLoadBitmap(LoadAndDisplayImageTask.java:229)
11-30 19:46:30.608: E/ImageLoader(22224): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.run(LoadAndDisplayImageTask.java:135)
11-30 19:46:30.608: E/ImageLoader(22224): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
11-30 19:46:30.608: E/ImageLoader(22224): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
11-30 19:46:30.608: E/ImageLoader(22224): at java.lang.Thread.run(Thread.java:856)
В чем ошыбка?
Не знаю.
DeleteВызов идет з адаптера который наследует ArrayAdapter у функции getView
DeleteВот код вызова лоадера:
String photoContact;
try {
photoContact = ContactsController.getPhotoUri(context, rowItem.getContactId()).toString();
ImageLoader.getInstance().displayImage(photoContact, holder.imagePhoto);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
И сама функция возврвта URI:
/**
* @return Return the contact photo URI by CONTACT_ID
*/
public static Uri getPhotoUri(Context context, int contactId) {
try {
Cursor cur = context.getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
null,
ContactsContract.Data.CONTACT_ID + "=" + contactId + " AND "
+ ContactsContract.Data.MIMETYPE + "='"
+ ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'", null,
null);
if (cur != null) {
if (!cur.moveToFirst()) {
return null; // no photo
}
} else {
return null; // error in cursor process
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
return Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
}
Использую дефолтовые настройки... з интернета грузит хорошо, а з самого телефона никак ((( Может я сто то не так делаю?
Отличная, качественная работа!
ReplyDeleteПривет. Подскажи пожалуйста. Очень часто выскакивает ошибка OutOfMemoryError по твоей инструкции в Github проблему решить не получилось.
ReplyDeleteПривет. Тогда вряд ли чем-то смогу помочь. Возможно где-то есть утечки памяти, попрофайли память.
DeleteЗдравствуй. Помоги плиз.
ReplyDeleteDisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.appicon)
.showImageOnFail(R.drawable.appicon)
.cacheInMemory(true)
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
.displayer(new RoundedBitmapDisplayer(20))
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(options)
.build();
ImageLoader.getInstance().init(config);
а вот фрагмент из xml
При таком xml картинка не отображается.
Но стоит только сделать например android:layout_height="50dp" картинка сразу отображается, но непропорционального размера, а растягивается на 50 в высоту и на весь экран в ширину. Но я хочу использовать wrap_content, и чтобы картинка отображалась не растянутой, а пропорциональной. Почему так не работает
Здравстуйте!
ReplyDeleteОбъясните, пожалуйста начинающему как в своем кастомизированном адаптере для списка заполнить у ViewHolder'а поле с изображением? Ведь получается, если я ничего не напутал, что библиотека работает только из активити (так как ей нужен контекст). Где тогда получать инстанс ImageLoader.getInstance() и задавать для него конфиг?
метод getView адаптера примерно такой:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.news_row_layout,
parent, false);
ViewHolder viewHolder = new ViewHolder();
viewHolder.title = (TextView) convertView.findViewById(R.id.news_name);
viewHolder.announce = (TextView) convertView.findViewById(R.id.news_annotation);
viewHolder.date = (TextView) convertView.findViewById(R.id.news_date);
viewHolder.imageNews = (ImageView) convertView.findViewById(R.id.image_of_news);
convertView.setTag(viewHolder);
}
ViewHolder holder = (ViewHolder) convertView.getTag();
EntryNews news = list.get(position);
holder.title.setText(news.getTitle());
holder.announce.setText(news.getAnnounce());
holder.date.setText(news.getDate());
// Как здесь при помощи библиотеки загрузить изображение в imageNews ?
return convertView;
}
Библиотека работает отовсюду, надо просто передать ей ImageView и URL картинки. Ну и опции возможно.
DeleteТ.е. как-то так - ImageLoader.getInstance().displayImage(imageUrl, viewHolder.imageNews)
Глобальную конфигурацию стоит настраивать с Application классе или в первой Activity.
Все это можно почитать в Readme и посмотреть в примерном проекте - https://github.com/nostra13/Android-Universal-Image-Loader/tree/master/sample
Приветствую!
ReplyDeleteimageLoader.init(ImageLoaderConfiguration.createDefault(context));
context - что это за переменная и для чего она?
.discCacheExtraOptions вроде как уже не нужен? Чем он отличается от .diskCacheExtraOptions и почему, когда я пытаюсь сделать .diskCacheExtraOptions(480, 800, CompressFormat.JPEG), он CompressFormat.JPEG подчеркивает красным, а при замене на Bitmap.CompressFormat.JPEG вообще отказывается работать. Как быть?
Добрый день!
ReplyDeleteКак загрузить картинку (BitMap) по линку?
Я пробовал так:
ImageLoader imageLoader = ImageLoader.getInstance();
if (!imageLoader.isInited()) {
imageLoader.init(ImageLoaderConfiguration.createDefault(context));
}
BitMap image = imageLoader.loadImageSync("Ссылка на картинку");
Но картинку не подгружает (((