寫在前面的幾句話
<p>
前幾天谷歌發布了android-support-library-23.2支持庫,這一次23.2版本增加了一些新的支持庫以及新的功能。接下來這篇文章,就是對這些新功能部分做簡單的用法介紹
這次更新增加的東西有:
- Support Vector Drawables and Animated Vector Drawables
- AppCompat DayNight theme
- Design Support Library: Bottom Sheets
- Support v4: MediaBrowserServiceCompat
- RecyclerView
- Custom Tabs
- Leanback for Android TV
需要添加的依賴如下:
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.android.support:design:23.2.0'
compile 'com.android.support:support-vector-drawable:23.2.0'
compile 'com.android.support:animated-vector-drawable:23.2.0'
分別說明使用方法
1.Support Vector Drawables and Animated Vector Drawables(支持矢量圖片和矢量圖片動畫)
<p>
其實前面的文章關于Metarial Design動畫中也有介紹到矢量圖與矢量圖的動畫,但是那是局限于5.0以上的設備才可以使用,沒想到這么快就Support包就向下支持了,其實使用方法也很類似,大家可以參考下那篇文章,
想在低版本使用還需要做點東西:
AndroidStudio1.4已經引入過矢量繪圖支持在構建時生成PNG圖像。要想禁用此功能(并想使用最新支持庫),你需要在你的build.gradle里面添加vectorDrawables.useSupportLibrary = true
// Gradle Plugin 2.0+
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
你會發現這個新的屬性只在AndroidStudio2.0版本存在。如果您使用的是AndroidStudio1.5,需要
// Gradle Plugin 1.5
android {
defaultConfig {
generatedDensities = []
}
// This is handled for you by the 2.0+ Gradle Plugin
aaptOptions {
additionalParameters "--no-version-vectors"
}
}
通常需要在三個xml文件中定義可動的矢量圖:
一個矢量圖使用<vector>元素,放在res/drawable/下。
一個可動的矢量圖使用<animated-vector>元素,放在res/drawable/下。
一個或更多個動畫對象使用<objectAnimator>元素,放在res/anim/下。
可動矢量圖可以使用<group>和<path>元素。<group>元素定義一系列路徑或者子組,<path>元素定義可繪圖的路徑。
當你定義了一個想要作用動畫的矢量可繪制圖,使用android:name屬性給每個group和path指定一個唯一的名字,這樣你可以從動畫的定義中找到他們。
res/drawable/vector_drawable_cpu_ani.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportHeight="600"
android:viewportWidth="600">
<group android:name="cpu_box">
<path
android:name="cpu"
android:fillColor="?android:textColorSecondary"
android:pathData="
M341.087,157.478 c7.417,0,13.435,6.018,13.435,13.435 v170.174c0,7.417-6.018,13.435-13.435,13.435 H170.913 c-7.417,0-13.435-6.018-13.435-13.435V170.913c0-7.417,6.018-13.435,13.435-13.435H341.087z
M390.348,157.478 c0-19.785-16.041-35.826-35.826-35.826H157.479c-19.785,0-35.826,16.041-35.826,35.826v197.043 c0,19.785,16.041,35.826,35.826,35.826h197.043c19.785,0,35.826-16.041,35.826-35.826V157.478z " />
</group>
<group android:name="bottom">
<path
android:name="wires_bottom"
android:fillColor="?android:textColorSecondary"
android:pathData="
M193.304,408.261V462h-17.913 v-53.739H193.304z
M264.957,408.261V462h-17.914v-53.739H264.957z
M300.783,408.261V462h-17.914v-53.739H300.783z
M229.13,408.261 V462h-17.913v-53.739H229.13z
M336.609,408.261V462h-17.914v-53.739H336.609z" />
</group>
<group android:name="top">
<path
android:name="wires_top"
android:fillColor="?android:textColorSecondary"
android:pathData="
M193.304,50v53.739h-17.913V50H193.304z
M264.957,50 v53.739h-17.914V50H264.957z
M300.783,50v53.739h-17.914V50H300.783z
M229.13,50v53.739h-17.913V50H229.13z
M336.609,50v53.739 h-17.914V50H336.609z " />
</group>
<group android:name="right">
<path
android:name="wires_right"
android:fillColor="?android:textColorSecondary"
android:pathData="
M408.261,318.695H462v17.914h-53.739V318.695z
M408.261,247.043H462v17.914h-53.739V247.043z
M408.261,211.217 H462v17.913h-53.739V211.217z
M408.261,282.869H462v17.914h-53.739V282.869z
M408.261,175.391H462v17.913h-53.739V175.391z" />
</group>
<group android:name="left">
<path
android:name="wires_left"
android:fillColor="?android:textColorSecondary"
android:pathData="
M50,318.695h53.739v17.914H50V318.695z
M50,247.043h53.739v17.914H50V247.043z
M50,211.217h53.739v17.913H50V211.217z
M50,282.869 h53.739v17.914H50V282.869z
M50,175.391h53.739v17.913H50V175.391z" />
</group>
</vector>
res/drawable/animated_cpu.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable_cpu_ani">
<target
android:name="top"
android:animation="@animator/pulse_top" />
<target
android:name="right"
android:animation="@animator/pulse_right" />
<target
android:name="left"
android:animation="@animator/pulse_left" />
<target
android:name="bottom"
android:animation="@animator/pulse_bottom" />
</animated-vector>
res/anim/anim_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="250"
android:propertyName="translateX"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:valueFrom="0"
android:valueTo="-10"
android:valueType="floatType" />
</set>
最后調用
//Support Vector Drawables
app:srcCompat="@drawable/vector_drawable_cpu_ani"
// Animated Vector Drawables
app:srcCompat="@drawable/animated_cpu"
至于pathData中的東西,其實前面也有說過SVG標準指令,這里就不做過多概述了
2.AppCompat DayNight theme(DayNight 主題)
<p>
之前有看到同事折騰這個DayNight模式折騰還蠻久,不過這次谷歌出了,倒是可以省去不少時間
DayNight主題包含DayNight.NoActionBar, DayNight.DarkActionBar, DayNight.Dialog 等等,DayNight主題支持應用切換 白天 和 夜晚 主題,根據 是否為 ‘夜晚’ 決定是否從白天主題有效的切換到夜晚主題。注意這里的DayNight主題只支持API14以上
使用方法如下:
首先要設置Application或者單獨Activity的theme為DayNight主題
themes.xml
<style name="AppTheme.DayNight.NoActionBar">
//other
</style>
AndroidManifest,xml
android:theme="@style/AppTheme.DayNight.NoActionBar"
DayNight主題有四個的模式:
MODE_NIGHT_NO 始終使用天(光)的主題
MODE_NIGHT_YES 始終使用夜間(黑暗)的主題
MODE_NIGHT_AUTO 根據一天中的時間晝/夜之間的變化。
MODE_NIGHT_FOLLOW_SYSTEM 此設置遵循系統的設置,基本上是以MODE_NIGHT_NO為主
我們可以通過我們通過調用AppCompatDelegate.setDefaultNightMode(),與getDelegate()。setLocalNightMode()來改變
但是兩者設置是有區別的,
- AppCompatDelegate.setDefaultNightMode()的設置是對整個App中theme為DayNight主題生效
- getDelegate().setLocalNightMode()的設置只對于設置的地方生效
AppCompatDelegate.setDefaultNightMode()
public void ChangeDayNight(View v){
switch (v.getId()){
case R.id.day:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
recreate();
break;
case R.id.night:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
recreate();
break;
}
}
getDelegate().setLocalNightMode()
public void ChangeDayNight(View v){
switch (v.getId()){
case R.id.day:
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
recreate();
break;
case R.id.night:
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
recreate();
break;
}
}
但是如果把AppCompatDelegate.setDefaultNightMode()與getDelegate().setLocalNightMode()寫在一起你就會發現一個有趣的情況
public void ChangeDayNight(View v){
switch (v.getId()){
case R.id.day:
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
recreate();
break;
case R.id.night:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
recreate();
break;
}
}
看圖可以發現,一開始都是白天模式的,然后我先設置了day及 getDelegate().setLocalNightMode()方法,然后設置了AppCompatDelegate.setDefaultNightMode()方法,但是這里的設置為night的方法并沒有生效,但是當跳轉到另外的一個界面的時候卻發現這個界面是夜晚的模式,所以其實AppCompatDelegate.setDefaultNightMode()方法是生效了的,那么為什么在第一個界面設置AppCompatDelegate.setDefaultNightMode()方法沒有生效呢?
我們看下AppCompatDelegate.setDefaultNightMode()這個方法前面的注釋
Sets the default night mode. This is used across all activities/dialogs but can be overriden locally via {@link #setLocalNightMode(int)}.
所以其實當前Activity中getDelegate().setLocalNightMode()方法設置后會覆蓋掉AppCompatDelegate.setDefaultNightMode()方法,導致看起來好像沒有生效,其實是已經生效了的。
3.Design Support Library : Bottom Sheets (材料設計,底部表)
<p>
Design Support Library 其實準備作為Metarial Design的第四篇文章進行說明的,這里提前說明Bottom Sheets的使用
Bottom Sheets 可以在Activity,Fragment,以及Dialog中使用,使用時候繼承的父類分別是:AppCompatActivity,AppCompatFragment,AppCompatDialog
先說明Bottom Sheets在Activity中的使用
1.創建Xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="bottomshets"
android:onClick="showBottom"
/>
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="@color/colorAccent"
app:behavior_hideable="false"
app:behavior_peekHeight="0dp"
app:layout_behavior="@string/bottom_sheet_behavior"
android:orientation="horizontal"/>
</android.support.design.widget.CoordinatorLayout>
布局很簡單有一個Button 和一個linearLayout,其實是把這個LinearLayout作為底部彈出的View,但是注意LinearLayout多出了幾個新的東西
- app:layout_behavior="@string/bottom_sheet_behavior" 這個屬性是作為Bottom Sheet必須的屬性
- app:behavior_hideable="false" 這個屬性是當我們拖拽下拉的時候,bottom sheet是否能全部隱藏
- app:behavior_peekHeight="0dp" 這個屬性是當Bottom Sheets關閉的時候,底部下表我們能看到的高度
另外這里必須用CoordinatorLayout才可以生效
2.代碼中
final BottomSheetBehavior behavior = BottomSheetBehavior.from(mlinearlayout);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
mlinearlayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
});
這里是通過附加一個BottomSheetBehavior 給CoordinatorLayout的子視圖,behavior.setState()是設置Bottom Sheet的狀態,Bottom Sheet的狀態有5種:
- STATE_COLLAPSED: 關閉Bottom Sheets,顯示peekHeight的高度,默認是0
- STATE_DRAGGING: 用戶拖拽Bottom Sheets時的狀態
- STATE_SETTLING: 當Bottom Sheets view擺放時的狀態。
- STATE_EXPANDED: 當Bottom Sheets 展開的狀態
- STATE_HIDDEN: 當Bottom Sheets 隱藏的狀態
我們可以通過setBottomSheetCallback來監聽Bottom Sheet的回調
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(View bottomSheet, int newState) {
}
@Override
public void onSlide(View bottomSheet, float slideOffset) {
}
});
onSlide方法是拖拽中的回調可以根據slideOffset做動畫,onStateChanged方法可以監聽到狀態的改變
至于Fragment的使用則與Activity中相似,就不做聲明,參考Activity的使用即可。
接下來說明下Dialong的使用方式:
final BottomSheetDialog dialog = new BottomSheetDialog(this);
View view = LayoutInflater.from(this).inflate(R.layout.bottomdialog_layout, null);
LinearLayout linearLayout = (LinearLayout)view.findViewById(R.id.dialog_layout);
linearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.setContentView(view);
dialog.show();
使用其實與普通的Dialog差不多
效果如下:
如果要在Bottom Sheets里面使用滑動的布局,則需要使用NestedScrollView, RecyclerView,或者ListView/ScrollView on API 21+
4.Support v4: MediaBrowserServiceCompat
<p>
該Support v4庫用作許多支持庫的基礎,并且為一些新版本介紹的特征提供支持(backports)。添加到以前發布的MediaSessionCompat類,為媒體播放了提供了堅實的基礎,這個版本增加了MediaBrowserServiceCompat和MediaBrowserCompat提供,帶來了最新的API兼容的解決方案(甚至沒有在L設備上加)支持API4+。這使得我們更容易地在Android上支持媒體的播放和在Android Wear上瀏覽媒體,為我們提供了一個標準的接口,讓你的媒體播放服務與界面連起來。
由于我不是很關心,所以沒有了解這方法的東西,跳過
5.RecyclerView
<p>
RecyclerView組件為我們提供了靈活的創建列表和網格以及動畫的特性。這個版本帶來一個激動人心的新特性LayoutManager API:自動測量!!!這允許RecyclerView尺寸大小根據其內容的大小尺寸定制。這意味著,先前不可用的情況,例如使用WRAP_CONTENT為尺寸的RecyclerView,現在都是可能的。你會發現所有內置的布局管理現在都支持自動測量。由于這種變化,一定要確保你的item的布局屬性:以前被忽視的布局參數(如MATCH_PARENT的滾動方向)現在將不一樣。如果你有一個自定義的LayoutManager并且不是基于之前的拓展,有一個選擇就是你可以調用setAutoMeasureEnabled(true),以及做一些小的變化(詳情見Javadoc)來支持新特性注意,雖然RecyclerView支持動畫,但是他不支持自己動畫邊界變化,如果你想對RecyclerView邊界進行動畫處理,你可以使用 Transition APIs.
這里變化從使用代碼層面來說沒有什么太多變化,就不上代碼了。
6.Custom Tabs(自定義選項卡)
爛尾很久了,這里看了下沒有太多資料,最近又比較忙,準備下次開篇專門的文章介紹下,好的就這樣了。