關於適配一直是Android開發者頭疼的問題,恰好這段時間遇到了這個問題,決定仔細的學習一下(瘋狂百度學習)。android
關於沉浸式,其實在安卓裏面並非沉浸式,只是對狀態欄和虛擬導航欄的透明化操做而已,可是市面上你們都這麼叫,因此就成爲了沉浸式導航欄了。(本文測試皆爲GoogleAPI的版本模擬器,所有親身測試過)bash
看了許多的博客和代碼,發現這個東西還真是夠複雜的,由於安卓太多的廠商有太多的ROM,以致於適配不過來。可是總的來講,沉浸式分爲四個部分:app
FLAG_TRANSLUCENT_STATUS
將狀態欄設置爲透明而且設置爲全屏,而後添加一個和StatusBar 同樣大小的View,將View 的 background 設置爲咱們想要的顏色,從而來實現沉浸式。window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
複製代碼
Android 5.0 (API 21)以上版本:在這個階段,系統加入了一個重要的屬性和方法:android:statusBarColor
(對應的方法爲setStatusBarColor),經過這個方法咱們就能夠實現沉浸式了。工具
Android 6.0 (API 23) 以上版本:爲啥要加這個階段咧,由於這個階段開始,咱們能夠改狀態欄的繪製模式,能夠顯示白色或淺黑色的內容和圖標。(深色或者淺色)佈局
這個階段沒有真正意義上的沉浸式,咱們只是將狀態欄透明化了而已。學習
沒有設置沉浸式時: 測試
咱們設置狀態欄爲透明,而且設置一個Status大小的view:ui
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
複製代碼
這裏咱們須要解釋一下:spa
當window的這個屬性有效的時候,會自動設置 system ui visibility的標誌
SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
複製代碼
咱們來看下效果:3d
能夠看到,狀態欄已經透明化了,而且全屏顯示了,可是這樣子啊,咱們的組件就直接顯示到最上面去了。咱們還須要設置一個和狀態欄同樣大小的View去填充,那麼,咱們能夠這麼實現
在佈局文件中填充一個View比做狀態欄
設置根佈局的paddingTop 屬性來填充狀態欄
當界面爲底部導航欄擁有多個Fragment時,咱們來看看適配的效果
沒有適配時
我門使用上面的Activity的適配方法適配後。
能夠看到,適配時成功的,可是,咱們須要把頂部填充一下。由於這個適配時針對頂部透明的。
沒有適配時
使用如下方法適配時
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//將側邊欄頂部延伸至status bar
drawer.setFitsSystemWindows(true);
//將主頁面頂部延伸至status bar;雖默認爲false,但經測試,DrawerLayout需顯示設置
drawer.setClipToPadding(false);
複製代碼
實現的效果
完美實現
那麼,針對安卓4.4到5.0的適配方法,咱們能夠封裝爲以下:
public class SimpleStatusBarAdapter {
/*
適配安卓4.4--5.0 測試手機 谷歌安卓模擬器
*/
public static void setAdapter(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/*
適配安卓4.4--5.0 測試手機 谷歌安卓模擬器 將跟佈局的paddingTop設置爲狀態欄的高度
*/
public static void setAdapter(Fragment fragment, View rootView, boolean headImage) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (!headImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
}
}
/*
適配安卓4.4--5.0 測試手機 谷歌安卓模擬器 側邊欄爲DrawerLayout時,設置這個屬性實現沉浸式
*/
public static void setAdapter(DrawerLayout drawer) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//將側邊欄頂部延伸至status bar
drawer.setFitsSystemWindows(true);
//將主頁面頂部延伸至status bar;雖默認爲false,但經測試,DrawerLayout需顯示設置
drawer.setClipToPadding(false);
}
}
}
複製代碼
從Android 5.0開始,安卓系統加入了一個比較重要的方法setStatusBarColor
(對應屬性:android:statusBarColor
),經過這個方法,能夠很輕鬆地實現沉浸式狀態欄。
未適配時
使用Android4.4時的適配方法的界面
能夠看到,設置以後只是透明化了一點。可是仍是沒有到理想的狀態。
咱們開始使用API21以後的方法來實驗一下:
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_color));
複製代碼
這裏須要解釋一下的是:
想要這個方法生效,必須還要配合一個Flag一塊兒使用,必須設置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,
而且不能設置FLAG_TRANSLUCENT_STATUS(Android 4.4才用這個)。
複製代碼
再來運行一下看下效果:
完美實現
也能夠在Theme中使用
<style name="MDTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/status_color</item>
</style>
複製代碼
若是想將狀態欄直接所有隱藏掉咱們能夠這麼設置
<style name="ImageTranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowTranslucentStatus">true</item>
<!-- 設置statusBarColor 爲透明-->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
還有
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_translution));
複製代碼
咱們看看效果:
這裏,咱們看到,底部的虛擬導航欄也被透明化了。
麼事,咱們有百度,咱們去修改<item name="android:windowTranslucentNavigation">false</item>
複製代碼
運行,,,結果和咱們想象的徹底不一樣,狀態欄也失效了。
可是,我找到了解決方法
activity.getWindow().getDecorView().
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
複製代碼
添加這個便可不透明化底部虛擬導航欄
咱們先來看看,徹底沒有適配的時候
能夠看到,狀態欄徹底擋住了咱們的視圖,咱們來設置上面的方式試試
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_color));
複製代碼
而後咱們會看到,emmm
咱們把狀態欄的顏色寫死了,那麼要適配動態的Fragment,咱們只能將狀態欄直接所有隱藏掉,直接這麼寫
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_translution));
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
複製代碼
而後,運行看看
能夠看到,確實是實現了。可是,這裏須要咱們適配頂部的高度
if (!headImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
複製代碼
咱們只須要實現一下方法便可完美實現沉浸式
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(activity.getResources().getColor(R.color.status_translution));
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
toolbar.setFitsSystemWindows(true);
複製代碼
如圖:
今天發現上面的適配方法有個問題,遇到水滴屏就會變形。而後,我又試出了新的適配方法。
首先,在NavigationView中加入
app:insetForeground="@null"
複製代碼
其餘的照上面適配 而後加上
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = context.getResources().getDimensionPixelSize(resourceId);
View headerView = navigationView.getHeaderView(0);
navigationView.setForeground(new BitmapDrawable());
coordinatorLayout.setPadding(0, height, 0, 0);
headerView.setPadding(headerView.getPaddingLeft(),headerView.getPaddingTop()+height,headerView.getPaddingRight(),headerView.getPaddingBottom());
coordinatorLayout.setBackground(toolbar.getBackground());
複製代碼
這樣子,就完美適配了,嘗試過水滴屏(小米),劉海屏(小米)都沒有啥問題
在這個階段,其實和5.0時差很少,只不過多了對狀態欄字色和圖標的顏色的操做API,由於以前的沉浸式不能設置狀態欄爲淺色調,否則就會看的很彆扭。
咱們來看看淺色時的界面
是否是以爲很彆扭,這種狀況在6.0以前是沒法解決的,在這以後,咱們就能夠完美解決了。
咱們只須要在5.0的基礎上加上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
複製代碼
看看效果
能夠看到,完美實現。
另外,還能夠在style中使用
<!-- Android 6.0以上 狀態欄字色和圖標爲淺黑色-->
<item name="android:windowLightStatusBar">true</item>
複製代碼
6.0版本之上跟5.0之上同樣的,因此就不用區分各類狀況了。
咱們常常看到
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
複製代碼
那麼其中的View.SYSTEM_UI_FLAG_HIDE_NAVIGATION都是些啥咧?每次都是網上查一下,而後直接粘貼複製使用,也沒多想,今天就來總結一下。
參數 | 意義 |
---|---|
SYSTEM_UI_FLAG_FULLSCREEN | 隱藏狀態欄 |
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 半透明狀態欄 |
SYSTEM_UI_FLAG_HIDE_NAVIGATION | 隱藏虛擬導航欄 |
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | 半透明虛擬導航欄,會自動設置半透明狀態欄 |
SYSTEM_UI_FLAG_IMMERSIVE | 自動隱藏狀態欄和虛擬導航欄 |
SYSTEM_UI_FLAG_IMMERSIVE_STIKY | 自動隱藏狀態欄和虛擬導航欄 |
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
複製代碼
類型 | 參數 | 意義 |
---|---|---|
int | FLAG_BLUR_BEHIND | 讓該window後全部東西都模糊(blur) |
int | FLAG_DIM_BEHIND | 讓該window後全部的東西都成暗淡(dim) |
int | FLAG_DITHER | 開啓抖動(dithering) |
int | FLAG_FORCE_NOT_FULLSCREEN | 恢復window非全屏顯示 |
int | FLAG_FULLSCREEN | 讓window進行全屏顯示 |
int | FLAG_KEEP_SCREEN_ON | 當該window對用戶可見時,讓設備屏幕處於高亮(bright)狀態。 |
int | FLAG_LAYOUT_IN_SCREEN | 讓window佔滿整個手機屏幕,不留任何邊界(border) |
int | FLAG_LAYOUT_NO_LIMITS | window大小再也不不受手機屏幕大小限制,即window可能超出屏幕以外,這時部份內容在屏幕以外。 |
int | FLAG_SECURE | 當該window在進行顯示的時候,不容許截屏。 |
咱們只須要將啓動時的背景設置爲透明,而後其餘的正常適配便可,這樣子的作法是適配瞞天過海的啓動頁
<item name="android:windowIsTranslucent">true</item>
複製代碼
咱們從前到後,測試了許多效果,那麼可不能夠封裝爲一個工具類,針對性的適配,咱們來嘗試下。
public static void setActivityAdapter(Activity activity,boolean isSetLightStatusBar) {
/*
適配安卓4.4--5.0
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
/*
適配安卓5.0以上
*/
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// //注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
/*
適配6.0以上狀態欄文字顏色,設置爲深色
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (isSetLightStatusBar)
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
複製代碼
public static void setFragmentAdapter(Fragment fragment, View rootView, boolean headerIsImage) {
/*
適配安卓4.4--5.0
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (!headerIsImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
}
/*
適配安卓5.0以上
*/
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
if (!headerIsImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
}
}
複製代碼
/*
toolbar.getBackground()
*/
public static void setNavigationViewAdapter(DrawerLayout drawer, Context context, NavigationView navigationView, View rootLayout, Drawable bg) {
/*
適配安卓4.4--5.0
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//將側邊欄頂部延伸至status bar
drawer.setFitsSystemWindows(true);
//將主頁面頂部延伸至status bar;雖默認爲false,但經測試,DrawerLayout需顯示設置
drawer.setClipToPadding(false);
}
/*
適配安卓5.0以上
*/
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = context.getResources().getDimensionPixelSize(resourceId);
View headerView = navigationView.getHeaderView(0);
navigationView.setForeground(new BitmapDrawable());
rootLayout.setPadding(0, height, 0, 0);
headerView.setPadding(headerView.getPaddingLeft(),headerView.getPaddingTop()+height,headerView.getPaddingRight(),headerView.getPaddingBottom());
rootLayout.setBackground(bg);
}
}
複製代碼
最終,我總結的是,咱們可使用工具類,可是,最終仍是須要本身應對部分適配不成功的問題,因此仍是須要本身去理一理,輪子雖好,可不要貪杯哦。
好了,這篇文章先到這裏,後期有新的的再補充(客套話)。