プレースホルダーとエラー状態
読み込み中や失敗時も自然に表示します。
「プレースホルダーとエラー状態」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。
読み込みはすぐには終わらない
ネットワーク上の画像の読み込みには時間がかかります。その間、何も処理しなければUIには何も表示されません。さらに、URLが壊れている、サーバーが停止している、ユーザーがオフラインであるなどの理由で、画像が届かないこともあります。
完成度の高いアプリでは、読み込み中にプレースホルダーを表示し、失敗したときはフォールバックを表示します。空白やクラッシュのままにはしません。Coilなら、どちらも簡単に実装できます。
このレッスンでは、プレースホルダー、エラー画像、状態ごとのカスタムUIを追加します。
シンプルなプレースホルダー
プレースホルダーを追加する最も簡単な方法は、painterResourceを使ってplaceholderパラメーターにドローアブルを渡すことです。Coilは実際の画像の読み込みが完了するまで、それを表示します。
灰色のボックスやロゴのシルエットなど、ニュートラルで軽量なドローアブルを使用してください。
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ImageWithPlaceholder(url: String) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray)
)
}エラー画像を追加する
errorパラメーターを使うと、リクエストが失敗したときにフォールバック用のドローアブルを表示できます。また、fallbackはmodelがnullの場合(読み込むデータがまったくない場合)に使用します。
プレースホルダー、エラー、フォールバックの3つを設定すれば、ほとんどコードを書かずに、あらゆる表示状態に対応できます。
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun RobustImage(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.no_image)
)
}ImageRequestでプレースホルダーを設定する
これらの状態はImageRequest自体にも設定できます。リクエストを一度作成して再利用する場合や、他のリクエストオプションも同時に指定したい場合に便利です。
リクエストレベルの関数には、ドローアブルリソースIDを渡します。
import android.content.Context
import coil3.request.ImageRequest
import coil3.request.crossfade
import coil3.request.error
import coil3.request.placeholder
fun buildRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.placeholder(R.drawable.placeholder_gray)
.error(R.drawable.image_broken)
.build()SubcomposeAsyncImageでカスタムUIを作る
ドローアブルで十分な場合もありますが、状態ごとにスピナー、シマー、テキストなどを表示したいこともあります。SubcomposeAsyncImageを使うと、loading、error、successに実際のコンポーザブルを指定できます。
柔軟性が高い一方で少し負荷が大きいため、長いリストではドローアブルのプレースホルダーを優先し、ヒーロー画像などに限定してsubcomposeを使ってください。
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage
@Composable
fun HeroImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = "Hero",
loading = { CircularProgressIndicator() },
error = { Text("Could not load image") }
)
}状態を直接読み取る
完全に制御したい場合は、rememberAsyncImagePainter APIを使い、そのstateを確認します。状態は、Loading、Success、Error、Emptyのケースを持つsealed型です。
これにより、結果に応じて独自のレイアウト、アニメーション、分析処理を制御できます。
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImagePainter
import coil3.compose.rememberAsyncImagePainter
@Composable
fun StateAwareImage(url: String) {
val painter = rememberAsyncImagePainter(model = url)
val state = painter.state.collectAsState().value
when (state) {
is AsyncImagePainter.State.Loading -> { /* show spinner */ }
is AsyncImagePainter.State.Error -> { /* show error UI */ }
else -> Image(painter = painter, contentDescription = null)
}
}シマーのプレースホルダーを表示する
よく使われるパターンにシマーがあります。これは、コンテンツがまもなく表示されることを示す、アニメーションする灰色のグラデーションです。ローディング状態の背後に単色のボックスを置くことで、簡単なバージョンを作成できます。
ここでは、読み込み中に色付きのボックスを表示するSubcomposeAsyncImageを使います。アニメーション効果が必要であれば、ボックスをシマー用ライブラリに置き換えてください。
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import coil3.compose.SubcomposeAsyncImage
@Composable
fun ShimmerImage(url: String) {
SubcomposeAsyncImage(
model = url,
contentDescription = null,
loading = {
androidx.compose.foundation.layout.Box(
Modifier.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceVariant)
)
}
)
}nullまたは空のModelを処理する
たとえばプロフィール画像のないユーザーのように、まだURLがない場合はどうすればよいでしょうか。nullをmodelとして渡すと、fallbackのドローアブルが表示されます(fallbackが設定されていない場合はerrorが表示されます)。
これにより、自分のコードでnullを特別扱いする必要がなくなります。Coilにデフォルトのアバターを表示させましょう。
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun ProfilePicture(photoUrl: String?) {
// If photoUrl is null, Coil shows the fallback default avatar.
AsyncImage(
model = photoUrl,
contentDescription = "Profile picture",
fallback = painterResource(R.drawable.default_avatar),
error = painterResource(R.drawable.default_avatar)
)
}Listenerでエラーを検知する
ログを記録したり再試行したりするために、読み込みに失敗したときコードで対応したい場合があります。ImageRequest.Builderのlistenerには、成功時とエラー時のコールバックを指定できます。
これらのコールバックでは、処理を軽く保ってください。メインスレッドで実行されます。
import android.content.Context
import android.util.Log
import coil3.request.ImageRequest
fun loggedRequest(context: Context, url: String) =
ImageRequest.Builder(context)
.data(url)
.listener(
onError = { _, result ->
Log.e("Coil", "Load failed", result.throwable)
},
onSuccess = { _, _ ->
Log.d("Coil", "Loaded $url")
}
)
.build()Tintとカラーフィルター
プレースホルダーやエラー用のドローアブルは、テーマに合わせて色を付けると見栄えがよくなることがあります。AsyncImageにColorFilterを適用すると、ライトモードとダークモードに合わせて変化させたい単色アイコン風のフォールバックに適しています。
フィルターは、色を変えたいプレースホルダーやエラー表示にだけ適用してください。実際の写真では通常、適用しません。
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import coil3.compose.AsyncImage
@Composable
fun TintedFallback(url: String?) {
AsyncImage(
model = url,
contentDescription = null,
error = painterResource(R.drawable.ic_image_placeholder),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant)
)
}堅牢な画像コンポーネント
すべての状態をきれいに処理する再利用可能なコンポーネントを組み立てましょう。読み込み中の背景、エラー時のフォールバック、クロスフェード、切り抜きを備えます。これをリストに組み込めば、空白が表示される問題を気にする必要はもうありません。
import androidx.compose.foundation.layout.size
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.res.painterResource
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
@Composable
fun ResilientImage(url: String?, size: Int = 96) {
val context = LocalContext.current
AsyncImage(
model = ImageRequest.Builder(context)
.data(url)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.placeholder_gray),
error = painterResource(R.drawable.image_broken),
fallback = painterResource(R.drawable.default_avatar),
modifier = Modifier.size(size.dp)
)
}クイックチェック
model = nullをAsyncImageに渡すと、Coilはデフォルトでどのdrawableを表示しますか?
まとめ
画像の読み込みを適切に処理できるようにしました。重要なポイントは次のとおりです。
- 読み込み中は
placeholder、失敗時はerror、モデルがnullの場合はfallbackが表示されます。 AsyncImageまたはImageRequestに設定できます。SubcomposeAsyncImageを使うと、状態ごとに実際のコンポーザブル(スピナーやシマーなど)を指定できます。rememberAsyncImagePainterを使うと、生のstateにアクセスして完全に制御できます。- リクエストの
listenerを使うと、読み込み結果をログに記録したり、それに応じて処理したりできます。
次はキャッシュとパフォーマンスです。同じ画像を2回目に読み込むとき、瞬時に表示できるようにします。
AI チューターと学ぶ Kotlin — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 36
- レッスン
- 152
よくある質問
「プレースホルダーとエラー状態」レッスンは無料ですか?
はい。「プレースホルダーとエラー状態」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。
「プレースホルダーとエラー状態」で何を学びますか?
読み込み中や失敗時も自然に表示します。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Android Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「プレースホルダーとエラー状態」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAndroid Academyレッスンでコードを書いて実行できますか?
はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Coilで画像を読み込む
- プレースホルダーとエラー状態
- キャッシュとパフォーマンス
- 音声と動画の再生