LoginSignup
50
43

More than 5 years have passed since last update.

CardViewでEdge-to-edgeなカードを作るときに気をつけるポイント

Posted at

概要

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

スクリーンショット 2015-09-07 18.47.38.png

Pre-Lollipop

スクリーンショット 2015-09-07 18.54.55.png

ポイント

cardUseCompatPaddingをtrueする

CardViewの cardUseCompatPadding のパラメータをtrueにすることで、Lollipop端末のCardViewのHorizontalPaddingに Elevationのサイズ分 の値が付与されて、VerticalPaddingに Elevationのサイズ✕1.5 の値が付与され、Pre-Lollipop端末のCardViewと同じ状態にすることができます。

CardViewApi21.java
@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);
}
RoundRectDrawableWithShadow.java
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_marginLeftlayout_marginRightElevationサイズ分のネガティブマージン を設定してlayout_marginToplayout_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

50
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
43