Material design中有一種很個性的設計概念:卡片式設計(Cards),這種設計與傳統的List Item有所區別,Cards包含更多的內容元素和擁有自己的UI特徵,關於Cards的設計規範可以參考官網介紹:
material.google.com/components/…
為了更好地實現這種 Cards UI 的設計,Google在v7包中引進了一種全新的控制元件:CardVew,本文將從開發的角度介紹CardView的一些常見使用細節。
Google用一句話介紹了CardView:一個帶圓角和陰影背景的FrameLayout。CardView在Android Lollipop(API 21)及以上版本的系統中適配較好,本文我們以一個具體的例子來學習CardView的基本使用和注意事項,效果圖如下:
上圖展示的是一個list列表,列表中的item使用了卡片式設計,主要利用CardView控制元件實現,為了精簡文章內容,這裡我們將item佈局中的核心程式碼羅列出來,加以分析:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView
tools:targetApi="lollipop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stateListAnimator="@drawable/lift_on_touch"
android:layout_marginLeft="@dimen/dp_8"
android:layout_marginRight="@dimen/dp_8"
android:layout_marginBottom="@dimen/dp_8"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="@dimen/dp_4"
app:cardUseCompatPadding="true"
app:cardPreventCornerOverlap="false">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- image、text等其他內容 -->
......
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>複製程式碼
可以看出,核心部分在於CardView的屬性使用,下面我們針對幾個特殊的屬性逐一分析,深化了解。
排版技巧
前面我們說過,CardView從本質上屬於FrameLayout,而CardView通常包含了較多的內容元素,為了方便地排版佈局中的各個元素,一般藉助於其他基本佈局容器,比如這裡我們使用了一個RelativeLayout
作為CardView的唯一Child。
陰影Padding
在Android Lollipop之前的系統,CardView會自動新增一些額外的padding空間來繪製陰影部分,這也導致了以Lollipop為分界線的不同系統上CardView的尺寸大小不同。為了解決這個問題,有兩種方法:第一種,使用不同API版本的dimension資源適配(也就是藉助values和values-21資料夾中不同的dimens.xml檔案);第二種,就是使用setUseCompatPadding
屬性,設定為true(預設值為false),讓CardView在不同系統中使用相同的padding值。
圓角覆蓋
這也是一個解決系統相容的問題。在pre-Lollipop平臺(API 21版本之前)上,CardView不會裁剪內容元素以滿足圓角需求,而是使用新增padding的替代方案,從而使內容元素不會覆蓋CardView的圓角。而控制這個行為的屬性就是cardPreventCornerOverlap,預設值為true。在本例中我們設定了該屬性為false。這裡我們看一下,在pre-Lollipop平臺中,不同cardPreventCornerOverlap值的效果對比圖(左false,右true):
顯然,預設值下自動新增padding的方式不可取,所以需要設定該屬性值為false。需要注意的一點是,該屬性的設定在Lollipop及以上版本的系統中沒有任何影響,除非cardUseCompatPadding的值為true。
Ripple效果
Cards一般都是可點選的,為此我們使用了foreground屬性並使用系統的selectableItemBackground值,同時設定clickable為true(如果在java程式碼中使用了cardView.setOnClickListener,就可以不用寫clickable屬性了),從而達到在Lollipop及以上版本系統中實現點選時的漣漪效果(Ripple),如圖:
在pre-Lollipop版本中,則是一個普通的點選變暗的效果,這裡就不截圖展示了,如果想改變老版本的點選效果,也可以通過版本相容的方式另行修改。
lift-on-touch
根據官網Material motion部分對互動動作規範的指導,Cards、Button等檢視應該有一個觸控抬起(lift-on-touch)的互動效果,也就是在三維立體空間上的Z軸發生位移,從而產生一個陰影加深的效果,與Ripple效果共同使用,官網給了一個很好的示例圖:
在實現這個效果也很簡單,可以在res/drawable
目錄下建立一個lift_on_touch.xml
檔案,內容如下:
<?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變化。這裡的6dp值也是有出處的,參考Google I/O 2014 app和Assign Elevation to Your Views。然後將其賦值給android:stateListAnimator
屬性即可。由於stateListAnimator
屬性只適用於Lollipop及以上版本,為了隱藏xml中的版本警告,可以指定tools:targetApi="lollipop"
。
關於這個功能,需要補充說明一點。這裡的lift_on_touch.xml
,嚴格意義上來講,屬於anim資源,同時適用於API 21及以上版本,所以按道理上來講應該將其放置在res/anim-v21
目錄下,然後使用@anim/lift_on_touch
賦值給stateListAnimator
屬性,而不是例子中的@drawable/lift_on_touch
方法。但是放置在res/anim-v21
目錄下會產生一個“錯誤”提示:
XML file should be in either "animator" or "drawable",not "anim"
雖然這個“錯誤”不影響編譯執行,但是對於追求完美主義的程式設計師們來說還是礙眼,所以本例中我選擇將其放在了res/drawable
目錄下,大家可以自行斟酌使用。
關於對lift-on-touch效果的理解,YouToBe網站有個視訊解說,感興趣的話可以參看看,地址如下:
DesignBytes: Paper and Ink: The Materials that Matter
總結說明
CardView還有一些其他屬性可供使用,比如cardElevation
設定陰影大小,contentPadding
代替普通android:padding
屬性等,比較基礎,本文就不一一介紹了,大家可以在官網上參考學習。從上面的介紹可以看出,在使用CardView時基本上都會用到一些標準配置的屬性,我們可以藉助style屬性,將其封裝到styles.xml
檔案中,統一管理,比如:
<style name="AppCardView" parent="@style/CardView.Light">
<item name="cardPreventCornerOverlap">false</item>
<item name="cardUseCompatPadding">true</item>
<item name="android:foreground">?attr/selectableItemBackground</item>
<item name="android:stateListAnimator" tools:targetApi="lollipop">@anim/lift_up</item>
......
</style>複製程式碼
最後,附上本文案例專案的GitHub地址:
歡迎關注我
本文由 亦楓 創作並首發於 亦楓的個人部落格 ,同步授權微信公眾號:技術鳥(NiaoTech),歡迎關注。