🔥

JetpackCompose 全画面スクロール対応のGridViewをImageUrlを使って作成する方法

2022/01/15に公開

現状のGridViewの問題

  1. 全画面スライドで一部コンテンツのみグリッド表示のようなレイアウトに対応できず、グリッド表示するにはそのコンテンツ部のみスライドするみたいなレイアウトになってしまう。
  2. なんだかややこしくて引数が何に対応しているのかわかりづらい(自分の知識不足なところがあるかもですが)

解決方法&サンプルコード

@Composable
fun SampleGridImageView (
        dataList: List<ImageContentEntity>
) {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .padding(8.dp),
    ) {
        item {
            FlowRow(
                mainAxisSize = SizeMode.Expand,
                mainAxisAlignment = FlowMainAxisAlignment.SpaceBetween,
                crossAxisAlignment = FlowCrossAxisAlignment.Center
            ) {
                dataList.forEachIndexed { _, item ->
                    item?.let {
                        ImageContent(
                            imageUrl = item.imageUrl,
                            usedItem = item.isUsedItem,
                            onClick = { route(item) }
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun ImageContent(
    imageUrl: String?,
    usedItem: Boolean = false,
    onClick: () -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val itemSize: Dp = LocalConfiguration.current.screenWidthDp.dp / 2 - 8.dp

    Box(
        modifier = Modifier
            .width(itemSize)
            .padding(8.dp)
            .animationClickable(
                interactionSource,
                onClick,
                !usedItem
            ),
        contentAlignment = Alignment.Center
    ) {
        FullSizeNetworkImage(
            imageUrl = imageUrl,
        )
        if (usedItem) {
            FullSizeNetworkImage(
                imageUrl = imageUrl,
                colorFilter = ColorFilter.tint(
                    Colors.Gray.copy(
                        alpha = 0.7F
                    )
                )
            )
            Text(
                text = "使用済み",
                color = Color.White,
                fontWeight = FontWeight.Bold
            )
        }
    }
}

@Composable
fun FullSizeNetworkImage(
    imageUrl: String?,
    modifier: Modifier = Modifier,
    colorFilter: ColorFilter? = null
) {
    Image(
        painter = rememberImagePainter(
            data = imageUrl,
            builder = {
                size(OriginalSize)
                scale(Scale.FIT)
            }
        ),
        contentDescription = null,
        contentScale = ContentScale.FillWidth,
        modifier = modifier.fillMaxWidth(),
        colorFilter = colorFilter
    )
}

やってること

  1. 全画面表示パディング16dpでスライドできる画面にImageUrlで取得した画像をグリッド表示
  2. 取得した画像は画面比率通り、取得する画像のサイズはバラバラを想定
  3. 小さい画像と大きい画像が横並びになる場合、大きい画像の高さを基準に小さい画像はV.Center表示(横幅は画面いっぱい指定なので大きい画像と同一サイズ)
  4. 使用済みItemはカラーフィルターと使用済み表示をする

解説

画面全体は8dpでパディングを取り、使い回すImageContent自体にパディング8dpを取ることで

全体16dpのパディングを保ちながらImageContent間の間も16dp取っている。

グリッド表示はGridViewを使う全画面スライドを実現することができないので

FlowRowを使用して実現する。

https://google.github.io/accompanist/flowlayout/

単純に大きさを決めてFlowするだけでは

画面サイズが違う場合に対応することができないので

LocalConfiguration.current.screenWidthDp

こちら使用して画面幅を取得する。

そして / 2 - 8.dpすることで

画面幅の半分+パディングマイナス分の大きさを取得している。


画面幅いっぱいにFlowするようにしているので

mainAxisAlignmentをSpaceBetween

crossAxisAlignmentをcenterにすることで

画像サイズが違うものが横並びになった場合、

大きいサイズの画像を基準に小さい画像がセンター表示になる。


画像の比率を保ちつつ大きさを最大にしているのは

builder = {
                size(OriginalSize)
                scale(Scale.FIT)
            }
        ),
        contentScale = ContentScale.FillWidth,
        modifier = modifier.fillMaxWidth(),

rememberImagePainterで画像を取得しているので

その中でbuilder size(OriginalSize)を使うことで

元画像のサイズを取得している。

そしてscale(Scale.FIT)とすることで

大きさを可変できるようし、

ImageWidgetの方にも

contentScale = ContentScale.FillWidthとした上で

modifier = modifier.fillMaxWidth()することで

最大幅を使うようにして高さはそれに合わせてscaleさせている。

バグがあって無理矢理対応している箇所がある

画像にカラーフィルターをかけて使用済み表示をしている所だが

単純にFullSizeNetworkImageに

colorFilterをかけて終わりにしたいが一つ問題があり、

colorFilterをかけるとalphaしてようがなんだろうが

元画像が消えてしまって元画像の上にフィルターをかけることができない

(なぜ???????)

なので仕方なくFullSizeNetworkImageの上に

もう一度FullSizeNetworkImage(フィルターをかけて透明になっているもの)を

重ねて表示して、その上にTextWidgetを表示することで実現している。

IntrinsicSize.Minで取得できそうなものだが

rememberImagePainterからのサイズは取得できないようで

IntrinsicSize.Minするとサイズ0のような扱いで

全部潰れてしまって豆粒みたいな表示になってしまう。


もっといい解決方法があれば教えて頂きたいです;;;;;;;

参考記事

https://medium.com/tech-takeaways/scrollable-grid-view-with-jetpack-compose-aed9f2b9c382

https://google.github.io/accompanist/flowlayout/

https://stackoverflow.com/questions/68908278/auto-height-jetpack-compose-coil-image

https://www.jetpackcompose.net/image-in-jetpack-compose

https://stackoverflow.com/questions/65103695/how-to-show-image-in-black-and-white-in-android-compose

https://ichi.pro/jetpack-compose-image-contentscaletype-kanzen-ni-zukai-13185329299189

Discussion