この記事は Goodpatch Advent Calendar 2018 18日目の記事です。
Google I/O 2018でMaterial Themingが発表され、最近のGoogle製アプリではGoogle独自のMaterial Designを採用したアプリが増えてきましたね。
また、その影響やiOS側のトレンドの変化もあって最近では、様々なアイコンやViewの形状が丸くなってきてたように思います。
そのあたりの考察は別の記事に譲るとして、今回はAndroid開発者として、角が丸めのCardViewを実装した時に困ったことと、その対処法をご紹介します。
cardCornerRadius=16dpのCardView
まずは最近流行りの丸いCardViewを作ります。
分かりやすいようにcardCornerRadiusは16dpにしておきましょう。
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="1dp">
はい、できました。簡単ですね。
Transition Animationがうまくいかない
このCardViewにShared Elementsを使ったTransition Animationを付けて別のActivityに遷移させてみます。
分かりやすいようにWindow Animation Scaleはx10です。
アニメーションが始まった瞬間にCardViewでClipされていた四角い画像が表に出てきているのが分かりますか?
Shared Elementを使ったときに遷移中に表示されるViewは遷移後のActivityのViewです。このためClipされていない遷移後のViewがタップ直後に表示されてしまいます。
これではせっかくのCardViewが少し残念です。
Transition中のViewのサイズに合わせてClipするradiusを変化させる
今回はCardViewの中に使うViewをpathでclipすることで滑らかにアニメーションさせるようにしてみました。
Transition Animation時にViewのサイズが変わるたびにonDrawが呼びだされるため、その時のViewの幅に合わせて少しずつRadiusが変化するようにします。
private val rootWidth = resources.displayMetrics.widthPixels
private val cornerRadius: Float = resources.getDimension(R.dimen.card_corner_radius)
private val cardSideMargin: Float = resources.getDimension(R.dimen.card_side_margin) * 2
override fun onDraw(canvas: Canvas) {
// viewの幅に合わせてcornerRadiusを変化させる
val radius = Math.min(cornerRadius, cornerRadius * ((rootWidth - this.width) / cardSideMargin))
clipPath.reset()
clipRectF.set(canvas.clipBounds)
clipPath.addRoundRect(clipRectF, radius, radius, Path.Direction.CW)
canvas.clipPath(clipPath)
super.onDraw(canvas)
}
今回のすべてのサンプルはこちらです。
ymegane/CardAnimationSample | GitHub
これで週末は心置きなく映画館に行けそうです。
みなさんよいクリスマスを。