Загрузка изображений с Coil
Асинхронная загрузка изображений в Compose
«Загрузка изображений с Coil» — бесплатный урок Android Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Зачем нужен загрузчик изображений
Большинство современных приложений отображает изображения из интернета: фотографии профилей, изображения товаров и миниатюры статей. Правильно загружать и декодировать такие изображения сложнее, чем кажется.
Нужно получать байты по сети в фоновом потоке, декодировать их в растровое изображение, кэшировать, чтобы не загружать повторно, и отменять запрос, если пользователь прокрутил изображение за пределы экрана. При ручной реализации легко допустить ошибки.
Coil (Coroutine Image Loader) делает всё это за вас с помощью компактного API, удобного для Compose. В этом уроке вы загрузите своё первое удалённое изображение.
Добавление Coil в проект
Coil поставляется как зависимость Gradle. Для Jetpack Compose нужен артефакт coil-compose, который предоставляет компонуемый элемент AsyncImage.
Добавьте его в build.gradle.kts вашего модуля. Coil 3 — текущая основная версия, работающая на разных платформах; именно интеграцию с Compose мы используем здесь.
// build.gradle.kts (module level)
dependencies {
implementation("io.coil-kt.coil3:coil-compose:3.0.4")
implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.4")
}Разрешение на доступ к интернету
Чтобы загружать изображения по URL, приложению нужно разрешение INTERNET. Это обычное разрешение, выдаваемое во время установки, поэтому его достаточно указать в манифесте. Запрос во время работы приложения не показывается.
Без этого разрешения каждый сетевой запрос завершается исключением безопасности.
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application ...>
...
</application>
</manifest>Ваш первый AsyncImage
Самый простой способ показать удалённое изображение в Compose — использовать AsyncImage. Передайте ему model (обычно строку с URL) и contentDescription, а обо всём остальном позаботится Coil: загрузит изображение в фоне, декодирует и отобразит его.
Компонент автоматически использует сопрограммы, связанные с композицией, поэтому запрос отменяется, если компонуемый элемент покидает экран.
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
@Composable
fun Avatar() {
AsyncImage(
model = "https://example.com/avatar.png",
contentDescription = "User avatar",
modifier = Modifier.size(96.dp)
)
}contentScale: как вписывается изображение
Изображения редко точно совпадают с размером отведенной им области. ContentScale управляет масштабированием растрового изображения внутри его границ.
ContentScale.Cropзаполняет область и обрезает выступающие части (отлично подходит для аватаров и миниатюр).ContentScale.Fitсохраняет изображение целиком, но может оставить пустое пространство.ContentScale.FillBoundsрастягивает изображение до границ (это может привести к искажению).
Для областей фиксированного размера чаще всего выбирают Crop.
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
@Composable
fun CircleAvatar(url: String) {
AsyncImage(
model = url,
contentDescription = "Avatar",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
)
}Создание запроса с помощью ImageRequest
Передать строку с URL — быстрый вариант. Для большего контроля создайте ImageRequest. Это позволяет задать такие параметры, как плавное появление, заголовки, преобразования и ключи кэша.
Вам понадобится Context, который Compose предоставляет через LocalContext.current.
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
@Composable
fun ProductImage(url: String) {
val context = LocalContext.current
val request = ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.build()
AsyncImage(
model = request,
contentDescription = "Product photo"
)
}Плавное появление при загрузке
По умолчанию изображение мгновенно появляется после завершения загрузки, что может выглядеть резко. crossfade(true) плавно проявляет изображение за короткое время.
Также можно указать длительность в миллисекундах, чтобы сделать проявление медленнее или быстрее. Такая небольшая деталь делает списки гораздо более качественными.
import coil3.request.ImageRequest
import coil3.request.crossfade
import android.content.Context
fun smoothRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.crossfade(durationMillis = 300)
.build()Загрузка из разных источников
Параметр model в AsyncImage отличается гибкостью. Coil умеет загружать изображения из многих источников, а не только по удалённым URL:
StringилиUriдля сетевых или локальных файлов.- Идентификатор ресурса drawable (
R.drawable.logo). Fileс устройства.- Контентный
Uriиз средства выбора фотографий.
Благодаря этому один и тот же компонуемый элемент одинаково удобно работает с удалёнными и локальными изображениями.
import android.net.Uri
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImage
@Composable
fun FlexibleImage(source: Any) {
// source can be a URL String, a Uri, a File, or a resource id
AsyncImage(
model = source,
contentDescription = null
)
}
// Examples:
// FlexibleImage("https://example.com/pic.jpg")
// FlexibleImage(Uri.parse("content://media/external/images/1"))Реакция на состояние загрузки
Иногда нужно знать, загружается ли изображение, успешно ли оно загружено или произошла ошибка, чтобы показать разный интерфейс. AsyncImagePainter предоставляет это состояние, но самый простой подход — использовать SubcomposeAsyncImage, который позволяет задать содержимое для каждого состояния.
В следующем уроке мы подробнее рассмотрим изображения-заполнители; здесь показана общая структура загрузки с учётом состояния.
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage
@Composable
fun StatefulImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = "Photo",
loading = { CircularProgressIndicator() }
)
}Отмена выполняется автоматически
Одно из главных преимуществ Coil — автоматическая отмена. Поскольку AsyncImage запускает запрос внутри композиции, при выходе компонуемого элемента с экрана, например когда строка прокручивается за пределы LazyColumn, выполняющийся запрос отменяется.
Это экономит трафик и CPU и предотвращает ненужную работу. Вам не нужно самостоятельно писать код отмены, поэтому Coil так хорошо подходит для длинных списков.
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
@Composable
fun PhotoFeed(urls: List<String>) {
LazyColumn {
items(urls) { url ->
AsyncImage(
model = url,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
)
}
}
}Полная карточка с изображением
Объединим изученное: создадим карточку с удалённым изображением, которое заполняет верхнюю часть, плавно появляется и обрезается по фиксированной высоте. Это базовый шаблон, который вы будете постоянно использовать повторно.
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
@Composable
fun ImageCard(title: String, imageUrl: String) {
val context = LocalContext.current
Card(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
Column {
AsyncImage(
model = ImageRequest.Builder(context)
.data(imageUrl)
.crossfade(true)
.build(),
contentDescription = title,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth().height(180.dp)
)
Text(text = title, modifier = Modifier.padding(16.dp))
}
}
}Быстрая проверка
Какой компонуемый элемент из coil-compose проще всего использовать для отображения удалённого изображения по URL в Jetpack Compose?
Итоги
Вы загрузили свои первые удалённые изображения с помощью Coil. Главное:
- Добавьте
coil-composeи сетевую зависимость OkHttp, а также разрешениеINTERNET. AsyncImage(model = url, contentDescription = ...)— быстрый вариант.- Используйте
ContentScale.Cropс фиксированным размером для аватаров и миниатюр. - Создайте
ImageRequestдля плавного появления и более точного управления. - Coil автоматически отменяет выполняющиеся запросы, когда компонуемые элементы покидают экран.
Далее: сделаем загрузку более аккуратной с помощью изображений-заполнителей и состояний ошибки.
Часто задаваемые вопросы
Урок «Загрузка изображений с Coil» бесплатный?
Да — полный текст урока «Загрузка изображений с Coil» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Загрузка изображений с Coil»?
Асинхронная загрузка изображений в Compose Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Загрузка изображений с Coil»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Загрузка изображений с Coil
- Заполнители и состояния ошибок
- Кэширование и производительность
- Воспроизведение аудио и видео