Что такое Glide и Coil и зачем они нужны?
В современной Android-разработке загрузка и отображение изображений из сети, локальных файлов или ресурсов — одна из самых частых задач. Однако просто скачать картинку и показать её в ImageView недостаточно. Нужно учитывать множество нюансов: кэширование (чтобы не тратить трафик), обработка ошибок (если картинка не загрузилась), трансформации (круглые аватары, размытие), поддержка GIF и видео, а также плавная анимация появления.
Glide и Coil — это две самые популярные библиотеки для загрузки изображений в Kotlin/Android. Они решают все перечисленные проблемы «из коробки».
- Glide — мощная, зрелая библиотека от Google (частично). Поддерживает GIF, видео, трансформации, анимации. Имеет огромное комьюнити.
- Coil — более современная, лёгкая и написанная полностью на Kotlin с использованием корутин. Отлично интегрируется с Jetpack Compose.
В этой статье мы рассмотрим обе библиотеки, так как они решают одни и те же задачи, но разными способами. Вы сможете выбрать ту, которая лучше подходит под ваш проект.
Установка
Добавьте зависимости в файл build.gradle.kts (уровень модуля app):
Для Glide:
dependencies {
implementation("com.github.bumptech.glide:glide:4.16.0")
kapt("com.github.bumptech.glide:compiler:4.16.0") // для GlideApp
}
Для Coil:
dependencies {
implementation("io.coil-kt:coil:2.6.0")
// Если используете Compose:
implementation("io.coil-kt:coil-compose:2.6.0")
}
После добавления синхронизируйте проект (Sync Now).
Быстрый старт
Давайте загрузим простую картинку из интернета в ImageView.
Пример с Glide:
// В Activity или Fragment
val imageView: ImageView = findViewById(R.id.my_image_view)
val url = "https://example.com/image.jpg"
Glide.with(this)
.load(url)
.into(imageView)
Пример с Coil:
// В Activity или Fragment
val imageView: ImageView = findViewById(R.id.my_image_view)
val url = "https://example.com/image.jpg"
imageView.load(url)
Как видите, Coil предлагает ещё более лаконичный синтаксис через extension-функцию load().
Основные методы и функции
Ниже приведены ключевые методы для работы с изображениями. Для каждого метода я покажу примеры для обеих библиотек.
1. load() — Загрузка изображения
Что делает: Основной метод, который принимает источник изображения (URL, URI, File, Drawable, Bitmap).
Параметры:
url: String— ссылка на изображение.uri: Uri— локальный URI.file: File— файл.resourceId: Int— ID ресурса (R.drawable...).
Пример (Glide):
Glide.with(this)
.load("https://example.com/photo.png")
.into(imageView)
Пример (Coil):
imageView.load("https://example.com/photo.png")
2. placeholder() — Изображение-заглушка
Что делает: Показывает временную картинку, пока основное изображение загружается.
Параметры:
placeholder: Int— ID ресурса drawable.
Пример (Glide):
Glide.with(this)
.load(url)
.placeholder(R.drawable.ic_loading)
.into(imageView)
Пример (Coil):
imageView.load(url) {
placeholder(R.drawable.ic_loading)
}
3. error() — Изображение при ошибке
Что делает: Показывает картинку, если загрузка основного изображения не удалась (нет интернета, битый URL).
Параметры:
error: Int— ID ресурса drawable.
Пример (Glide):
Glide.with(this)
.load(url)
.error(R.drawable.ic_error)
.into(imageView)
Пример (Coil):
imageView.load(url) {
error(R.drawable.ic_error)
}
4. override() — Принудительный размер
Что делает: Изменяет размер изображения до указанных ширины и высоты (в пикселях). Полезно для экономии памяти.
Параметры:
width: Int— ширина.height: Int— высота.
Пример (Glide):
Glide.with(this)
.load(url)
.override(300, 200)
.into(imageView)
Пример (Coil):
imageView.load(url) {
size(300, 200)
}
5. centerCrop() / fitCenter() — Масштабирование
Что делает: Определяет, как изображение будет вписано в ImageView.
centerCrop()— обрезает края, чтобы заполнить контейнер.fitCenter()— вписывает целиком, возможно с пустыми полями.
Пример (Glide):
Glide.with(this)
.load(url)
.centerCrop()
.into(imageView)
Пример (Coil):
imageView.load(url) {
crossfade(true)
transformations(CenterCrop())
}
6. circleCrop() — Круглая обрезка
Что делает: Превращает изображение в круг (идеально для аватарок).
Пример (Glide):
Glide.with(this)
.load(url)
.circleCrop()
.into(imageView)
Пример (Coil):
imageView.load(url) {
transformations(CircleCropTransformation())
}
7. skipMemoryCache() — Отключение кэша
Что делает: Запрещает кэширование изображения в оперативной памяти. Полезно для одноразовых картинок.
Параметры:
skip: Boolean— true, чтобы отключить кэш.
Пример (Glide):
Glide.with(this)
.load(url)
.skipMemoryCache(true)
.into(imageView)
Пример (Coil):
imageView.load(url) {
memoryCachePolicy(CachePolicy.DISABLED)
}
8. diskCacheStrategy() — Стратегия дискового кэша (только Glide)
Что делает: Управляет кэшированием на диске.
Параметры:
DiskCacheStrategy.ALL— кэшировать все версии.DiskCacheStrategy.NONE— не кэшировать.DiskCacheStrategy.DATA— только исходные данные.DiskCacheStrategy.RESOURCE— только обработанные.
Пример (Glide):
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView)
9. listener() — Отслеживание загрузки
Что делает: Позволяет реагировать на успешную загрузку или ошибку.
Пример (Glide):
Glide.with(this)
.load(url)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
println("Ошибка загрузки: ${e?.message}")
return false
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
println("Изображение загружено!")
return false
}
})
.into(imageView)
Пример (Coil):
imageView.load(url) {
listener(
onStart = { println("Загрузка началась") },
onSuccess = { _, _ -> println("Успешно") },
onError = { _, _ -> println("Ошибка") }
)
}
10. crossfade() — Анимация появления
Что делает: Добавляет плавное появление изображения (fade-in).
Параметры:
duration: Int— длительность анимации в миллисекундах (по умолчанию 300).
Пример (Glide):
Glide.with(this)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade(500))
.into(imageView)
Пример (Coil):
imageView.load(url) {
crossfade(true)
// или crossfade(500) для кастомной длительности
}
Реальный практический пример
Представьте, что вы делаете приложение-галерею. Нужно загрузить список картинок из API и отобразить их в RecyclerView. Вот как это будет выглядеть с Coil (с Glide аналогично):
// Адаптер для RecyclerView
class ImageAdapter(private val imageUrls: List<String>) : RecyclerView.Adapter<ImageAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val url = imageUrls[position]
holder.imageView.load(url) {
placeholder(R.drawable.ic_loading)
error(R.drawable.ic_error)
crossfade(true)
transformations(CenterCrop())
size(400, 400)
}
}
override fun getItemCount() = imageUrls.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.image_view)
}
}
В этом примере мы:
- Используем placeholder и error для UX.
- Добавляем crossfade для плавности.
- Применяем CenterCrop для единообразного вида.
- Ограничиваем размер до 400x400, чтобы не перегружать память.
Советы и типичные ошибки
Советы:
- Всегда используйте placeholder и error. Пользователь не должен видеть пустое место или битую ссылку.
-
Также в библиотеке
OAuth клиент для Kotlin: библиотека Ktor OAuth для аутентификации в API
Работа с Excel в Swift: Полное руководство по библиотекам и инструментам для iOS/macOS
AddressSanitizer для C++: полное руководство с примерами
Hibernate для Java: полное руководство с примерами
bcrypt для Go: полное руководство с примерами
Рекомендаций