概要
CardViewを上手く使うと、Pre-Lollipop端末にもElevation風な影をカードに付けることができるためUIを実装する際に非常に役に立ちます。
CardViewを使ってEdge-to-edge(Widthが画面の端から端まであるよう)なカードを実装した際に、Pre-Lollipop端末とLollipop端末の間で微妙にPaddingに差異が出る問題にハマったので、今回はそのハマった点と解決のポイントを紹介します。
ハマったときの状態
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
card_view:cardBackgroundColor="@color/white"
card_view:cardCornerRadius="0dp"
card_view:cardElevation="2dp"
card_view:contentPadding="16dp">
...
Lollipop
Pre-Lollipop
ポイント
cardUseCompatPaddingをtrueする
CardViewの cardUseCompatPadding
のパラメータをtrueにすることで、Lollipop端末のCardViewのHorizontalPaddingに Elevationのサイズ分 の値が付与されて、VerticalPaddingに Elevationのサイズ✕1.5 の値が付与され、Pre-Lollipop端末のCardViewと同じ状態にすることができます。
@Override
public void updatePadding(CardViewDelegate cardView) {
if (!cardView.getUseCompatPadding()) {
cardView.setShadowPadding(0, 0, 0, 0);
return;
}
float elevation = getMaxElevation(cardView);
final float radius = getRadius(cardView);
int hPadding = (int) Math.ceil(RoundRectDrawableWithShadow
.calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
int vPadding = (int) Math.ceil(RoundRectDrawableWithShadow
.calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap()));
cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding);
}
static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
boolean addPaddingForCorners) {
if (addPaddingForCorners) {
return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
} else {
return maxShadowSize * SHADOW_MULTIPLIER;
}
}
static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
boolean addPaddingForCorners) {
if (addPaddingForCorners) {
return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
} else {
return maxShadowSize;
}
}
Elevationのサイズ分Marginを調整する
cardUseCompatPadding
をtrueにするだけだとすべての端末が、ハマったときのPre-Lollipopの状態になるだけなので、次は地道にElevationのサイズ分Marginを調整する必要があります。
若干イケてないやり方ですが...
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-2dp"
android:layout_marginRight="-2dp"
android:layout_marginTop="13dp"
card_view:cardBackgroundColor="@color/white"
card_view:cardCornerRadius="0dp"
card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true"
card_view:contentPadding="16dp">
具体的には layout_marginLeft
と layout_marginRight
に Elevationサイズ分のネガティブマージン を設定してlayout_marginTop
や layout_marginBottom
から Elevationサイズ✕1.5 の値を引いてあげればOKです。
これで、バージョン別にレイアウトファイルを分けることなく、すべての端末でEdge-to-edgeなCardViewをキレイに作ることができます。
参考にしたページ
https://developer.android.com/reference/android/support/v7/widget/CardView.html
http://stackoverflow.com/questions/29068430/cardview-has-extra-margin-in-each-edge-on-pre-lollipop