背景
上篇文章一個(gè)千萬量級(jí)的APP使用的一些第三方庫中,在說到一個(gè)使用很廣泛的滑動(dòng)退出庫SwipeBackLayout時(shí)有提過有時(shí)間會(huì)分享自己在項(xiàng)目中引入這個(gè)庫的時(shí)候填過的一些坑。前段時(shí)間項(xiàng)目加入沉浸式狀態(tài)欄效果的時(shí)候也走了不少?gòu)澛罚覂烧呓Y(jié)合效果還不錯(cuò),就一起分享出來了。
先上效果圖:

一、添加 SwipeBackLayout
compile 'me.imid.swipebacklayout.lib:library:1.0.0'
step1:BaseActivity
新建一個(gè)繼承SwipeBackActivity的BaseActivity類,SwipeBackActivity的代碼請(qǐng)看SwipeBackLayout庫源碼,在onCreate方法內(nèi)添加如下兩行代碼。所有繼承BaseActivity的頁面都能具有滑動(dòng)效果了。但比如登錄頁和主頁面等部分不需要右滑效果的Activity可以通過setSwipeBackEnable(false)方法禁用右滑效果。
mSwipeBackLayout = getSwipeBackLayout();
// 設(shè)置滑動(dòng)方向,可設(shè)置EDGE_LEFT, EDGE_RIGHT, EDGE_ALL,EDGE_BOTTOM
mSwipeBackLayout.setEdgeTrackingEnabled(SwipeBackLayout.EDGE_LEFT);
step2:設(shè)置theme
在style.xml文件中添加以下代碼
<!-- 設(shè)置右滑主題 -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowAnimationStyle">@style/HoloThemeActivityAnimation</item>
<!-- 設(shè)置背景透明,右滑時(shí)才能看到上一個(gè)界面,否則會(huì)看到黑屏效果-->,
<item name="android:windowIsTranslucent">true</item>
</style>
<!--Activity退出動(dòng)畫-->
<style
name="HoloThemeActivityAnimation" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>
</style>
<!--主界面單獨(dú)設(shè)置以下主題,不透明,否則右滑不是顯示上一個(gè)頁面而是直接顯示桌面了~-->
<style name="AppThemeNoTranslucent" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">false</item>
</style>
step3:AndroidManifest
在AndroidManifest文件中 application節(jié)點(diǎn)設(shè)置全局主題
android:theme="@style/AppTheme"。
為主界面單獨(dú)設(shè)置不透明主題
android:theme="@style/AppThemeNoTranslucent"。
二、加入沉浸式狀態(tài)欄效果
先上效果圖:

關(guān)于沉浸式狀態(tài)欄這個(gè)資料現(xiàn)在很多,但感覺很多介紹的并不全面,我自己也試過挺多方法后才有了目前這個(gè)方案。
step1:首先得設(shè)置狀態(tài)欄透明
關(guān)于狀態(tài)欄透明最開始用的是以下方法,這也是目前在網(wǎng)上看到的比較多的一種方法,但是后來發(fā)現(xiàn)在部分5.0以上系統(tǒng)上會(huì)有兼容性問題:
//以下方案在部分5.0以上手機(jī)內(nèi)會(huì)有兼容性問題,會(huì)有一個(gè)半透明的背景附在狀態(tài)欄位置上
final int sdk = Build.VERSION.SDK_INT;
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
if (sdk >= Build.VERSION_CODES.KITKAT) {
int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; // 設(shè)置透明狀態(tài)欄
if ((params.flags & bits) == 0) {
params.flags |= bits;
window.setAttributes(params);
}
}
發(fā)現(xiàn)這個(gè)問題在百度音樂和360衛(wèi)士等APP上也是存在的,看以下兩個(gè)截圖。(可能我的測(cè)試機(jī)有點(diǎn)非主流~,在小米,華為等主流手機(jī)上以上方法和這兩個(gè)APP測(cè)試都正常)


后來在Android 系統(tǒng)狀態(tài)欄沉浸式/透明化完整解決方案這篇文章的評(píng)論內(nèi)下面發(fā)現(xiàn)了下面這個(gè)方法??梢詫?shí)現(xiàn)完美的狀態(tài)欄透明效果。
//最終方案
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//5.0 全透明實(shí)現(xiàn)
//getWindow.setStatusBarColor(Color.TRANSPARENT)
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//4.4 全透明狀態(tài)欄
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
step2:設(shè)置標(biāo)題欄的高度
狀態(tài)欄透明后本來以為這事情差不多了,不過事實(shí)證明這僅僅是另外一個(gè)坑的開始。
設(shè)置狀態(tài)欄透明后,ToolBar(或者自定義的titleBar)就會(huì)跑到StatusBar下面,如圖1。處理這個(gè)問題有兩個(gè)思路。一個(gè)是為ToolBar頂部設(shè)置一個(gè)高度和狀態(tài)欄一樣的padding或margin。第二個(gè)是在根布局設(shè)置android:fitsSystemWindows="true"屬性,然后新建一個(gè)和ToolBar背景色一樣的View添加到原來狀態(tài)欄的位置(或者直接在根布局設(shè)置一個(gè)和ToolBar一樣的背景色,不過測(cè)試時(shí)發(fā)現(xiàn)這個(gè)方法在有DrawerLayout時(shí)好像不管用)。

第一種方法
為ToolBar頂部設(shè)置一個(gè)高度和狀態(tài)欄一樣的padding或margin
1.1 一個(gè)坑
最開始我使用的是第一種方法,在ToolBar外面套一層布局,然后為ToolBar設(shè)置頂部margin。但是發(fā)現(xiàn)了一個(gè)很奇葩的問題,在4.4以上系統(tǒng)上底部聊天及評(píng)論框不能被系統(tǒng)輸入法頂上去。如下圖2(圖片引用自另外兩種android沉浸式狀態(tài)欄實(shí)現(xiàn)思路,關(guān)于這個(gè)問題更詳細(xì)的描述也可以點(diǎn)擊此鏈接)。

原因是因?yàn)樵O(shè)置狀態(tài)欄透明后得設(shè)置android:fitsSystemWindows="true"這個(gè)屬性。當(dāng)時(shí)我的解決辦法是使用AndroidBug5497Workaround 動(dòng)態(tài)計(jì)算布局的高度。這個(gè)方法在華為等手機(jī)上存在兼容性問題。我在源碼的基礎(chǔ)上做了一些修改,修改后的效果在華為,小米,三星等手機(jī)上測(cè)試均正常。修改后的代碼看我另外一篇文章關(guān)于AndroidBug5497Workaround方法的兼容性問題。
1.2 含有DrawerLayout+NavigationView
這里還要注意的一個(gè)問題是如果頁面含有DrawerLayout,而且側(cè)滑菜單欄使用的是NavigationView,記得給NavigationView加上app:insetForeground="#00000000"。否則側(cè)滑時(shí)會(huì)出現(xiàn)一個(gè)半透明的狀態(tài)欄。如下圖3。

1.3 兼容4.4
另外就是在4.4系統(tǒng)上如果使用DrawerLayout+NavigationView,在NavigationView上會(huì)出現(xiàn)下圖4情況。解決辦法如下(參考自Android 系統(tǒng)狀態(tài)欄沉浸式/透明化完整解決方案):
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//將側(cè)邊欄頂部延伸至status bar
drawer.setFitsSystemWindows(true);
//將主頁面頂部延伸至status bar;雖默認(rèn)為false,但經(jīng)測(cè)試,DrawerLayout需顯示設(shè)置
drawer.setClipToPadding(false);
}

第二種方法
在根布局設(shè)置android:fitsSystemWindows="true"屬性,然后新建一個(gè)和ToolBar背景色一樣的View添加到原來狀態(tài)欄的位置(或者直接在根布局設(shè)置一個(gè)和ToolBar一樣的背景色,不過測(cè)試時(shí)發(fā)現(xiàn)這個(gè)方法在有DrawerLayout時(shí)好像不管用)
2.1 什么是fitsSystemWindows
使用第二種方法前我們有必要先了解下fitsSystemWindows這個(gè)屬性是什么。
System windows 指的就是屏幕上status bar、 navigation bar等系統(tǒng)控件所占據(jù)的部分。
android:fitsSystemWindows="true" 這個(gè)屬性的作用就是通過設(shè)置View的padding,使得應(yīng)用的content部分——Activity中setContentView()中傳入的就是content——不會(huì)與system window重疊。
這個(gè)屬性的詳細(xì)介紹看這里為什么我們要用fitsSystemWindows
2.2 封裝好的代碼
“新建一個(gè)和ToolBar一樣背景色的View添加到原來狀態(tài)欄的位置” 的代碼如下(參考自Android 沉浸狀態(tài)欄):
public static void setStateBarColor(Activity activity) {
// 設(shè)置狀態(tài)欄顏色
ViewGroup contentLayout = (ViewGroup) activity.findViewById(android.R.id.content);
setupStatusBarView(activity, contentLayout, Color.parseColor("#FF5677FC"));
// 設(shè)置Activity layout的fitsSystemWindows
View contentChild = contentLayout.getChildAt(0);
contentChild.setFitsSystemWindows(true);//等同于在根布局設(shè)置android:fitsSystemWindows="true"
}
private static void setupStatusBarView(Activity activity, ViewGroup contentLayout, int color) {
View mStatusBarView = null;
View statusBarView = new View(activity);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
contentLayout.addView(statusBarView, lp);
mStatusBarView = statusBarView;
mStatusBarView.setBackgroundColor(color);
}
/** * 獲得狀態(tài)欄高度 */
private static int getStatusBarHeight(Context context) {
int resourceId =context.getResources().getIdentifier("status_bar_height", "dimen", "android");
return context.getResources().getDimensionPixelSize(resourceId);
}
使用方法:
在相應(yīng)Activity中調(diào)用setStateBarColor(activity)方法即可。
2.3 缺陷
但是這種方法有個(gè)缺陷是在DrawerLayout上當(dāng)側(cè)滑菜單NavigationView滑出來時(shí)狀態(tài)欄會(huì)浮在上面。如下圖3。目前并未找到解決方案。

總結(jié):
可以看到兩種方式都不算是完美,實(shí)際項(xiàng)目中可以根據(jù)需要選擇合適的方式。
1.有DrawerLayout時(shí)優(yōu)先選擇第一種為ToolBar設(shè)置Padding或Margin的方式。
2.底部有輸入框的聊天和評(píng)論界面優(yōu)先選擇在根布局設(shè)置android:fitsSystemWindows="true"屬性,然后新建一個(gè)和ToolBar一樣背景色的View添加到原來狀態(tài)欄的位置的方式。當(dāng)然你也可以選擇統(tǒng)一用第一種方式,然后使用AndroidBug5497Workaround動(dòng)態(tài)調(diào)整輸入框的位置。
最后
源碼已上傳到GitHub點(diǎn)擊查看。