はじめに
一覧を角丸のカードにまとめ、各行をタップできるようにしました。通常時はきれいな角丸です。しかし、一番上や一番下の行を押すと、タップ時のリップルがカードの角より外側に見えました。
原因は、背景を角丸で描くことと、子要素の描画を角丸に収めることが別だったためです。
背景だけを角丸にしていた
最初の構成を小さくすると、次のようになります。
@Composable
fun ItemList(items: List<String>, onItemClick: (String) -> Unit) {
val shape = RoundedCornerShape(12.dp)
Column(
modifier = Modifier
.fillMaxWidth()
.background(Color(0xFFF2F2F2), shape),
) {
items.forEach { item ->
Text(
text = item,
modifier = Modifier
.fillMaxWidth()
.clickable { onItemClick(item) }
.padding(16.dp),
)
}
}
}
background(..., shape) は背景を指定した形で描きます。一方、行の clickable が表示するリップルまで、その形で切り取る指定ではありません。Android Developers の Modifier 一覧
(https://developer.android.com/develop/ui/compose/modifiers-list)でも、background は背景の描画、clip は内容の切り取りとして別々に定義されています。
親の Column をクリップする
カード全体の角丸に子要素の描画も合わせたいので、親に clip を追加します。
@Composable
fun ItemList(items: List<String>, onItemClick: (String) -> Unit) {
val shape = RoundedCornerShape(12.dp)
Column(
modifier = Modifier
.fillMaxWidth()
.clip(shape)
.background(Color(0xFFF2F2F2), shape),
) {
items.forEach { item ->
Text(
text = item,
modifier = Modifier
.fillMaxWidth()
.clickable { onItemClick(item) }
.padding(16.dp),
)
}
}
}
これで、一番上と一番下の行から出るリップルも、親の角丸の内側に収まります。元になった修正も、親の Column に clip(RoundedCornerShape(...)) を1行追加するものでした。
ポイントは Modifier の順序です。Compose では、同じ Modifier でも並べる順序によって結果が変わります。公式ドキュメント
(https://developer.android.com/develop/ui/compose/modifiers)には、clickable と padding
の順序でタップ可能な範囲が変わる例があります。見た目、リップル、余白の範囲をそれぞれ意識して並べる必要があります。
確認するときは角を押す
カードの中央の行を押しても、この問題は見つけにくいです。確認するなら、次の場所を長押ししてリップルを見るのが確実です。
- 一番上の行の左上・右上
- 一番下の行の左下・右下
- 区切り線や子要素も、意図した範囲に収まっているか
background(shape) だけで角丸のカードが完成したように見えても、子要素のタップ表現まで確認しておくと安心です。