Material design中有一種很個性的設計概念:卡片式設計(Cards),這種設計與傳統(tǒng)的List Item有所區(qū)別特铝,Cards包含更多的內(nèi)容元素和擁有自己的UI特征蛮瞄,關于Cards的設計規(guī)范可以參考官網(wǎng)介紹:
https://material.google.com/components/cards.html#
為了更好地實現(xiàn)這種 Cards UI 的設計久脯,Google在v7包中引進了一種全新的控件:CardVew,本文將從開發(fā)的角度介紹CardView的一些常見使用細節(jié)霉撵。
Google用一句話介紹了CardView:一個帶圓角和陰影背景的FrameLayout磺浙。CardView在Android Lollipop(API 21)及以上版本的系統(tǒng)中適配較好,本文我們以一個具體的例子來學習CardView的基本使用和注意事項徒坡,效果圖如下:
上圖展示的是一個list列表撕氧,列表中的item使用了卡片式設計,主要利用CardView控件實現(xiàn)喇完,為了精簡文章內(nèi)容伦泥,這里我們將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等其他內(nèi)容 -->
......
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
可以看出奄喂,核心部分在于CardView的屬性使用铐殃,下面我們針對幾個特殊的屬性逐一分析,深化了解跨新。
排版技巧
前面我們說過富腊,CardView從本質(zhì)上屬于FrameLayout,而CardView通常包含了較多的內(nèi)容元素域帐,為了方便地排版布局中的各個元素赘被,一般借助于其他基本布局容器,比如這里我們使用了一個RelativeLayout
作為CardView的唯一Child肖揣。
陰影Padding
在Android Lollipop之前的系統(tǒng)民假,CardView會自動添加一些額外的padding空間來繪制陰影部分,這也導致了以Lollipop為分界線的不同系統(tǒng)上CardView的尺寸大小不同龙优。為了解決這個問題羊异,有兩種方法:第一種,使用不同API版本的dimension資源適配(也就是借助values和values-21文件夾中不同的dimens.xml文件)彤断;第二種野舶,就是使用setUseCompatPadding
屬性,設置為true(默認值為false)宰衙,讓CardView在不同系統(tǒng)中使用相同的padding值平道。
圓角覆蓋
這也是一個解決系統(tǒng)兼容的問題。在pre-Lollipop平臺(API 21版本之前)上供炼,CardView不會裁剪內(nèi)容元素以滿足圓角需求一屋,而是使用添加padding的替代方案,從而使內(nèi)容元素不會覆蓋CardView的圓角袋哼。而控制這個行為的屬性就是cardPreventCornerOverlap冀墨,默認值為true。在本例中我們設置了該屬性為false先嬉。這里我們看一下轧苫,在pre-Lollipop平臺中,不同cardPreventCornerOverlap值的效果對比圖(左false疫蔓,右true):
顯然含懊,默認值下自動添加padding的方式不可取,所以需要設置該屬性值為false衅胀。需要注意的一點是岔乔,該屬性的設置在Lollipop及以上版本的系統(tǒng)中沒有任何影響,除非cardUseCompatPadding的值為true滚躯。
Ripple效果
Cards一般都是可點擊的雏门,為此我們使用了foreground屬性并使用系統(tǒng)的selectableItemBackground值嘿歌,同時設置clickable為true(如果在java代碼中使用了cardView.setOnClickListener,就可以不用寫clickable屬性了)茁影,從而達到在Lollipop及以上版本系統(tǒng)中實現(xiàn)點擊時的漣漪效果(Ripple)宙帝,如圖:
在pre-Lollipop版本中,則是一個普通的點擊變暗的效果募闲,這里就不截圖展示了步脓,如果想改變老版本的點擊效果,也可以通過版本兼容的方式另行修改浩螺。
lift-on-touch
根據(jù)官網(wǎng)Material motion部分對交互動作規(guī)范的指導靴患,Cards、Button等視圖應該有一個觸摸抬起(lift-on-touch)的交互效果要出,也就是在三維立體空間上的Z軸發(fā)生位移鸳君,從而產(chǎn)生一個陰影加深的效果,與Ripple效果共同使用患蹂,官網(wǎng)給了一個很好的示例圖:
在實現(xiàn)這個效果也很簡單或颊,可以在res/drawable
目錄下建立一個lift_on_touch.xml
文件,內(nèi)容如下:
<?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>
即通過屬性動畫動態(tài)改變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
目錄下會產(chǎn)生一個“錯誤”提示:
<selector> XML file should be in either "animator" or "drawable",not "anim"
雖然這個“錯誤”不影響編譯運行起胰,但是對于追求完美主義的程序員們來說還是礙眼升酣,所以本例中我選擇將其放在了res/drawable
目錄下,大家可以自行斟酌使用煞茫。
關于對lift-on-touch效果的理解帕涌,YouToBe網(wǎng)站有個視頻解說摄凡,感興趣的話可以參看看,地址如下:
DesignBytes: Paper and Ink: The Materials that Matter
總結(jié)說明
CardView還有一些其他屬性可供使用蚓曼,比如cardElevation
設置陰影大小亲澡,contentPadding
代替普通android:padding
屬性等,比較基礎纫版,本文就不一一介紹了谷扣,大家可以在官網(wǎng)上參考學習。從上面的介紹可以看出捎琐,在使用CardView時基本上都會用到一些標準配置的屬性会涎,我們可以借助style屬性,將其封裝到styles.xml
文件中瑞凑,統(tǒng)一管理末秃,比如:
<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地址: