Material Design系列教程(12) - CardView

簡介

CardViewAndroid 5.0 引進的一種卡片式布局控件,是一個帶有圓角和陰影效果的FrameLayout

CardView

CardView 的文檔中可以看到,它是存在于 support.v7 包中的,因此引進方式為:

implementation 'com.android.support:cardview-v7:<version>'

CardView 具備 材料設計特性陰影(Elevation)Z軸位移,目的就是突出不同元素之間的層次關系,看起來有立體的感覺。因此其使用場景一般是用在需要凸顯層次性內容布局,比如在顯示列表或網格布局時,使用 CardView 就可以方便用戶依據這些邊緣特征更容易去區分內容主體。

CardView 屬性簡析

下面列舉一些 CardView屬性特性:

屬性 釋義
CardView_cardBackgroundColor 設置背景顏色
CardView_cardCornerRadius 設置圓角大小
CardView_cardElevation 設置z軸的陰影
CardView_cardMaxElevation 設置z軸陰影的最大高度值
CardView_contentPadding 設置內容的內邊距
CardView_contentPaddingBottom 設置內容的底內邊距
CardView_contentPaddingLeft 設置內容的左內邊距
CardView_contentPaddingRight 設置內容的右內邊距
CardView_contentPaddingTop 設置內容的上內邊距
CardView_cardUseCompatPadding 是否使用CompatPadding
CardView_cardPreventCornerOverlap 是否取消圓角覆蓋

這里著重對 CardView 的兩個屬性講解一下:

  • CardView_cardUseCompatPadding - 陰影內邊距
    如果你為 CardView 指定了具體的大小,在 Android Lollipop 之前的系統,CardView 會自動添加一些額外的padding空間來繪制陰影部分,導致內容元素在 Lollipop 之前與之后的系統中大小可能不同。
    要解決這個問題,一個方法是使用不同 AP I版本的 dimension 資源適配(也就是借助 valuesvalues-21 文件夾中不同的 dimens.xml 文件),或者,如果你想要為 CardViewLollipop 及其之后的系統上同樣增加一個內邊距,可以通過設置CardView_cardUseCompatPadding="true"(其默認值為false)。

  • CardView_cardPreventCornerOverlap - 圓角覆蓋
    在 pre-Lollipop 平臺(API 21版本)之前,CardView 不會裁剪內容元素以滿足圓角需求,而是增加一個padding,從而使內容元素不會覆蓋 CardView 的圓角。而控制這個行為的屬性就是cardPreventCornerOverlap,默認值為true
    :該屬性在 Lollipop 及以上版本的系統中沒有任何影響,除非設置了cardUseCompatPadding="true"。通常為了兼容低版本,讓 CardView 在所有系統版本表現一致,則需要把CardView_cardPreventCornerOverlap設置為false,取消(低版本)自動添加padding效果。但是這樣設置后,內容元素與 CardView 的圓角就會重疊到一起,導致圓角消失(被內容元素覆蓋)。因此,如果還想要有圓角效果,考慮對內容元素進行圓角裁剪。

綜上,為了系統兼容,使得高低版本系統中,CardView 的表現具備一致性,通常要添加如下兩個配置:

//為 Lollipop 及其以上版本增加一個陰影padding
CardView_cardUseCompatPadding="true"
//取消 Lollipop 以下版本的padding
CardView_cardPreventCornerOverlap="false"

其他注意事項

  • CardView 無法使用android:background設置背景,可以通過app:cardBackgroundColor屬性進行設置。

  • 在 Android Lollipop之前的系統中,CardView 自動為內容元素添加一個padding作為陰影效果。這個padding的大小為:
    ? 左右內邊距:maxCardElevation + (1 - cos45) * cornerRadius
    ?上下內邊距:maxCardElevation * 1.5 + (1 - cos45) * cornerRadius

由于padding已被用作陰影效果,因此,你無法使用android:paddingCardView 增加一個內邊距,而是應當通過配置XML方式:CardView_contentPaddingXXX或者代碼調用setContentPadding(int,int,int,int)進行設置。

CardView 使用

首先給出 CardView 最簡布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <android.support.v7.widget.CardView
        android:layout_width="300dp"
        android:layout_height="300dp"
        app:cardPreventCornerOverlap="false"
        app:cardUseCompatPadding="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FFFF00"
            android:gravity="center"
            android:text="I am in CardView"
            android:textSize="20sp" />
    </android.support.v7.widget.CardView>

</LinearLayout>

效果如下:

CardView

因為沒有對 CardView 進行特殊設置,這里它表現出來的就是一個ViewGroup的效果,只能用來容納子控件。

下面我們為 CardView 增加一些屬性設置,來看下效果:

  • 圓角效果:app:cardCornerRadius="10dp",效果如下:
CardView_radius
  • 陰影效果:app:cardElevation="10dp",效果如下:
CardView_elevation
  • 漣漪效果(Ripple):
android:clickable="true"
android:foreground="?attr/selectableItemBackground"

漣漪效果需要點擊產生,因此這里首先要求 Cards 是可點擊的(clickable="true")。

效果如下圖所示:

CardView_ripple

漣漪效果(Ripple)只在 Android 5.0 之后有效,在pre-Lollipop版本中,則是一個普通的點擊變暗的效果。

如果想同時實現高低版本的漣漪效果(Ripple),可以通過自定義 CardView 前景:具體原理是,在 Android 5.0 及其之后版本,直接使用ripple即可。在 Android 5.0 之前,沒有ripple,則采用inset進行代替。

具體做法如下:

  • Android 5.0 及其之后版本
  1. 首先創建一個 selector,位置:drawable-v21/card_foreground_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#18ffc400"/>
        </shape>
    </item>
    <item android:state_focused="true" android:state_enabled="true">
        <shape android:shape="rectangle">
            <solid android:color="#0f000000"/>
        </shape>
    </item>
</selector>
  1. 然后創建一個ripple,引用上面的 selector,位置:drawable-v21/card_foreground.xml
<ripple xmlns:android="http://schemas.android.com/apk/res/android" 
android:color="#20000000"
android:drawable="@drawable/card_foreground_selector" />
  • Android 5.0 之前版本
  1. 同樣首先創建一個 selector,位置:drawable/card_foreground_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#1838f508"/>
            <corners android:radius="@dimen/card_radius" />
        </shape>
    </item>
    <item android:state_focused="true" android:state_enabled="true">
        <shape android:shape="rectangle">
            <solid android:color="#0f000000"/>
            <corners android:radius="@dimen/card_radius" />
        </shape>
    </item>
</selector>
  1. 然后創建一個inset,引用上面的 selector,位置:drawable/card_foreground.xml
<inset xmlns:android="http://schemas.android.com/apk/res/android" 
    android:drawable="@drawable/card_foreground_selector"
    android:insetLeft="2dp"
    android:insetRight="2dp"
    android:insetTop="4dp"
    android:insetBottom="4dp"/>
  • 動畫效果:根據官網 Material motion 部分對交互動作規范的指導,Cards、Button 等視圖應該有一個觸摸抬起( lift-on-touch)的交互效果,也就是在三維立體空間上的Z軸發生位移,從而產生一個陰影加深的效果,與Ripple效果共同使用。
    要實現 lift-on-touch 動畫效果,首先需要設置一個 selector,位于:res/drawable
<?xml version="1.0" encoding="utf-8"?>
<!-- animate the translationZ property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_enabled="true"
        android:state_pressed="true">
        <set>
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="translationZ"
                android:valueTo="6dp"
                android:valueType="floatType"/>
        </set>
    </item>
    <item>
        <set>
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:propertyName="translationZ"
                android:valueTo="0"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

其實就是通過屬性動畫動態改變translationZ值,沿著Z軸,從0dp到6dp變化。

最后通過android:stateListAnimator="@drawable/lift_on_touch"設置給 CardView

效果如下:

lift_on_touch

總結

一般使用 CardView 時,通常都會設置其漣漪效果(Ripple),lift_on_touch 動畫效果以及高低版本系統兼容性等等,這些基本上算是標準配置屬性,那么我們就可以將這些屬性配置放入到一個 style 中,直接提供給 CardView 使用即可。

    <style name="AppCardView" parent="@style/CardView.Light">
        <item name="cardPreventCornerOverlap">false</item>
        <item name="cardUseCompatPadding">true</item>
        <item name="android:clickable">true</item>
        <item name="android:foreground">?attr/selectableItemBackground</item>
        <item name="android:stateListAnimator" tools:targetApi="lollipop">@drawable/lift_on_touch</item>
    </style>

最后通過style="@style/AppCardView"設置給 CardView 即可。

示例

最后結合一個例子,來看下 CardView 的顯示效果。

例子:使用 RecyclerView 結合 CardView 進行圖片展示,效果如下所示:

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容