簡介
CardView 是 Android 5.0 引進的一種卡片式布局控件,是一個帶有圓角和陰影效果的FrameLayout
。
從 CardView 的文檔中可以看到,它是存在于 support.v7 包中的,因此引進方式為:
implementation 'com.android.support:cardview-v7:<version>'
CardView 具備 材料設計特性,陰影(Elevation)和 Z軸位移,目的就是突出不同元素之間的層次關系,看起來有立體的感覺。因此其使用場景一般是用在需要凸顯層次性內容布局,比如在顯示列表或網格布局時,使用 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 資源適配(也就是借助 values 和 values-21 文件夾中不同的 dimens.xml 文件),或者,如果你想要為 CardView 在 Lollipop 及其之后的系統上同樣增加一個內邊距,可以通過設置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:padding
為 CardView 增加一個內邊距,而是應當通過配置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 進行特殊設置,這里它表現出來的就是一個ViewGroup
的效果,只能用來容納子控件。
下面我們為 CardView 增加一些屬性設置,來看下效果:
- 圓角效果:
app:cardCornerRadius="10dp"
,效果如下:
- 陰影效果:
app:cardElevation="10dp"
,效果如下:
- 漣漪效果(Ripple):
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
漣漪效果需要點擊產生,因此這里首先要求 Cards 是可點擊的(clickable="true"
)。
效果如下圖所示:
漣漪效果(Ripple)只在 Android 5.0 之后有效,在pre-Lollipop版本中,則是一個普通的點擊變暗的效果。
如果想同時實現高低版本的漣漪效果(Ripple),可以通過自定義 CardView 前景:具體原理是,在 Android 5.0 及其之后版本,直接使用ripple
即可。在 Android 5.0 之前,沒有ripple
,則采用inset
進行代替。
具體做法如下:
- Android 5.0 及其之后版本:
- 首先創建一個 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>
- 然后創建一個
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 之前版本:
- 同樣首先創建一個 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>
- 然后創建一個
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。
效果如下:
總結
一般使用 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 進行圖片展示,效果如下所示: