Coilで画像を読み込む
Composeで非同期に画像を読み込みます。
「Coilで画像を読み込む」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。
画像ローダーが必要な理由
現代のアプリの多くは、プロフィール写真、商品画像、記事のサムネイルなど、インターネット上の画像を表示します。こうした画像を正しくダウンロードしてデコードするのは、見た目以上に難しい作業です。
ネットワーク経由でバイト列をバックグラウンドスレッドに取得し、ビットマップにデコードし、再取得しないようにキャッシュし、ユーザーがスクロールして対象から離れたらリクエストをキャンセルする必要があります。これを手作業で行うと、エラーの原因になりやすくなります。
Coil(Coroutine Image Loader)は、Composeで使いやすい小さなAPIですべてを処理してくれます。このレッスンでは、初めてリモート画像を読み込みます。
プロジェクトにCoilを追加する
CoilはGradleの依存関係として提供されています。Jetpack Composeでは、AsyncImageコンポーザブルを提供するcoil-composeアーティファクトを使用します。
モジュールの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()さまざまなソースから読み込む
AsyncImageのmodelは柔軟です。CoilはリモートURLだけでなく、多くの種類のソースから読み込めます。
- ネットワークまたはローカルファイルを指定する
StringやUri。 - ドローアブルリソースID(
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"))読み込み状態に応じて対応する
画像の読み込み中、成功、失敗のどの状態かを確認し、それぞれ異なるUIを表示したい場合があります。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))
}
}
}確認テスト
Jetpack ComposeでURLからリモート画像を表示する最も簡単なcoil-composeのコンポーザブルはどれでしょうか。
まとめ
Coilを使って、初めてリモート画像を読み込みました。重要なポイントは次のとおりです。
coil-composeとOkHttpのネットワーク依存関係を追加し、INTERNET権限も設定します。AsyncImage(model = url, contentDescription = ...)が最も手軽な方法です。- アバターやサムネイルには、固定サイズと
ContentScale.Cropを使用します。 - クロスフェードなど、より細かく制御するには
ImageRequestを作成します。 - コンポーザブルが画面から離れると、Coilは実行中のリクエストを自動的にキャンセルします。
次は、プレースホルダーとエラー状態を使って読み込み中の表示を改善します。
よくある質問
「Coilで画像を読み込む」レッスンは無料ですか?
はい。「Coilで画像を読み込む」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。
「Coilで画像を読み込む」で何を学びますか?
Composeで非同期に画像を読み込みます。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Android Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「Coilで画像を読み込む」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAndroid Academyレッスンでコードを書いて実行できますか?
はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Coilで画像を読み込む
- プレースホルダーとエラー状態
- キャッシュとパフォーマンス
- 音声と動画の再生