タイトル通りです。 普通にPNGとかなら楽勝ですが、Gifだとちょっと工夫が必要だったのでとうか
Coilについて
概要
サーバURLから画像を取ってきて表示する ということが簡単にできるライブラリです。
コード
静的な画像は単純なので、ここではgifで取ってみる
// implementation("io.coil-kt:coil-compose:2.3.0") // 通常はこっち
implementation("io.coil-kt:coil-gif:2.3.0")
@Composable
fun CoilOnlySample() {
val url = "https://play.pokemonshowdown.com/sprites/ani/pikachu.gif"
val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
.components {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.build()
AsyncImage(
modifier = Modifier,
model = ImageRequest.Builder(LocalContext.current)
.data(url)
.crossfade(true)
.build(),
imageLoader = imageLoader,
placeholder = painterResource(R.drawable.ic_placeholder),
contentDescription = "",
contentScale = ContentScale.Fit
)
}
下の画像はPngなので動いてないですが、アプリでは動いている
keyPoint
- ImageRequest
画像をリクエストする上で必要な情報を詰め込むと、指定したURLで画像をダウンロードしてくれる。
ImageRequests are value objects that provide all the necessary information for an ImageLoader to load an image.
- ImageLoader
ざっくり、リクエストした画像をどんな画像として処理するかを定義できるオブジェクト
ImageLoaders are service objects that execute ImageRequests. They handle caching, data fetching, image decoding, request management, bitmap pooling, memory management, and more. New instances can be created and configured using a builder:
Paletteについて
概要
画像から色を抽出することができる。 デフォルトだと以下種類の色を取ってくる様子
- Light Vibrant
- Vibrant
- Dark Vibrant
- Light Muted
- Muted
- Dark Muted
コード
android {
// 28 or higher
compileSdkVersion 28
// ...
}
dependencies {
implementation('androidx.palette:palette:1.0.0')
// ...
}
長くなるので一部端折りますが、
@Preview
@Composable
fun PaletteOnlySample() {
val context = LocalContext.current
/* Convert our Image Resource into a Bitmap */
val bitmap = remember {
BitmapFactory.decodeResource(context.resources, com.example.core.R.drawable.sample)
}
/* Create the Palette, pass the bitmap to it */
val palette = remember { Palette.from(bitmap).generate() }
LazyColumn(
modifier = Modifier
~
~
) {
item { Text("rgb", fontSize = 24.sp) }
item {
LazyRow(
contentPadding = PaddingValues(vertical = 12.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
item { Circle(palette.lightVibrantSwatch?.let { Color(it.rgb) } ?: Color.White, hint = "lightVibrantSwatch") }
~
~
}
item { Text("Text" , fontSize = 24.sp) }
item {
Text(
text = "Sample Title Text", fontSize = 20.sp, fontWeight = FontWeight.Bold,
color = palette.lightVibrantSwatch?.let { Color(it.titleTextColor) } ?: Color.White)
}
item {
Text(
text = "Sample Body Text", fontSize = 20.sp, fontWeight = FontWeight.Bold ,
color = palette.lightVibrantSwatch?.let { Color(it.bodyTextColor) } ?: Color.White)
}
item { Text("Population", fontSize = 24.sp) }
item { Text(text = "the number of pixels represented by this swatch") }
~
~
item { Text("Sample Image", fontSize = 24.sp) }
item { Image(painter = painterResource(id = com.example.core.R.drawable.sample_), contentDescription = "") }
}
}
@Composable
fun Circle(
color : Color,
hint: String = "hint"
) {
Box(
modifier = Modifier
.size(60.dp)
.clip(CircleShape)
.background(color = color)
) {
Text(text = hint, modifier = Modifier.align(Alignment.Center))
}
}
それぞれ、rgbだったり、テキストだったり、Pixel数が取れます。(なぜかLight Mutedがnullになってますが、、)
CoilからGif取得してPalettで色抽出
Coilから取得するGifをBitmapに変換する
ざっと見た感じ、PalettがBitmap以外の形式を受け入れてくれないので、Coilから取得するGif画像からBitmapを取得する方法があれば良い。
で、ちょいはまりどころがあったがGifからだと以下しかない様子
前準備 前述のPaletteOnlySampleを書き換え。 単純に外からBitmapを入れられるようにしたのみ(UI表示のため)
fun PaletteOnlySample(_bitmap: Bitmap? = null) {
val context = LocalContext.current
/* Convert our Image Resource into a Bitmap */
val bitmap = remember {
_bitmap ?: BitmapFactory.decodeResource(context.resources, R.drawable.sample)
}
以下にて取得できる
はまりどころはコメント↓
@Composable
fun CoilAndPaletteSample(
url: String = "https://play.pokemonshowdown.com/sprites/ani/pikachu.gif"
) {
val context = LocalContext.current
var bitmap: Bitmap? by remember { mutableStateOf(null) }
val imageLoader = ImageLoader.Builder(context)
.components {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.build()
/*
Gif からの取得は現状、別で画像をリクエストして取得 -> bitmapとして変換
という手順が必要
*/
LaunchedEffect(Unit) {
val req = ImageRequest.Builder(context)
.data(url)
.allowHardware(false)
.build()
val result = req.context.imageLoader.execute(req)
if (result is SuccessResult) {
bitmap = result.drawable.toBitmap() // Bitmap保管
}
}
// gif でないならこれだけでもいける
// val painter = rememberAsyncImagePainter(
// model = url,
// onState = { state ->
// when(state) {
// is AsyncImagePainter.State.Success -> {
// Log.d(TAG, "CoilAndPaletteSample: ${state.result.drawable.toBitmap()}")
// bitmap = state.result.drawable.toBitmap()
// }
// else -> Unit
// }
// }
// )
// Image(painter = painter, contentDescription = "")
Column {
AsyncImage(
modifier = Modifier,
model = ImageRequest.Builder(LocalContext.current)
.data(url)
.crossfade(true)
// .target( 2.0.0 以降はrequest.target must be null. というエラー
// onSuccess = { result ->
//
// }
// )
.build(),
imageLoader = imageLoader,
placeholder = painterResource(R.drawable.ic_placeholder),
contentDescription = "",
contentScale = ContentScale.Fit
)
bitmap?.let {
PaletteOnlySample(it)
}
}
}
コメントにも書いてますが、Gif画像のみをロードしてBitmap変換が必要な様子。
理由は上述のtarget(onSuccess(result))
が使えないため。(これが使えればresult.toBitmap()でとれた)
gifじゃなければrememberAsyncImagePainter
のみでいける
rgb みる限り取れてますね。
全体はこちら
https://github.com/kkhouse/Playground/tree/main/AndroidProject/Other/CoilAndPaletteSample
余談
ChatGPTに聞こうと思ったが良いプロンプトが浮かばなかったので、手作業ったはまった。。