在開發(fā)應(yīng)用的過程中关串,會遇見一些狀態(tài)欄和導(dǎo)航欄相關(guān)的問題拧廊,在此記錄一下。本文主要目的是記錄我在開發(fā)中遇到的狀態(tài)欄和導(dǎo)航欄相關(guān)的問題晋修,以便日后查看方便吧碾,如果可以幫到你,真是再好不過了墓卦。
本文主要分為以下幾個(gè)部分:
1. 基本概念
首先需要明白幾個(gè)概念:
- 狀態(tài)欄(StatusBar):指屏幕最頂端倦春,顯示時(shí)間、電量落剪、推送圖標(biāo)那一欄睁本,每個(gè)手機(jī)都有狀態(tài)欄。
- 標(biāo)題欄(TitleBar):指狀態(tài)欄下忠怖,顯示“返回鍵”呢堰、“標(biāo)題文字”那一欄,根據(jù)需求而定是否有標(biāo)題欄脑又,標(biāo)題欄可以使用
Toolbar
或者ActionBar
控件實(shí)現(xiàn) -
導(dǎo)航欄(NavigationBar):指屏幕最下端暮胧,有“返回鍵”、“Home鍵”问麸、”菜單鍵“那一欄往衷。導(dǎo)航欄是虛擬的,不是每個(gè)手機(jī)都有的严卖。
狀態(tài)欄席舍、標(biāo)題欄、導(dǎo)航欄可以參照下圖:
statusbar&navigationbar.png
-
沉浸式模式(Immersive Mode):沉浸式就是要給用戶提供完全沉浸的體驗(yàn)哮笆,使用戶有一種置身于虛擬世界之中的感覺来颤。 ---- Android狀態(tài)欄微技巧,帶你真正理解沉浸式模式
沉浸式模式本質(zhì)上就是把狀態(tài)欄稠肘、導(dǎo)航欄隱藏福铅,將應(yīng)用界面全屏化。
最常見的沉浸式模式應(yīng)用在游戲和視頻類應(yīng)用中项阴,比如愛奇藝的全屏播放滑黔,如下圖所示:
aiqiyi.png
-
透明狀態(tài)欄:如下圖所示,透明狀態(tài)欄就是讓應(yīng)用界面背景利用系統(tǒng)狀態(tài)欄空間环揽,讓應(yīng)用界面背景和系統(tǒng)狀態(tài)欄融為一體略荡。
transparentstatusbar.png
需要注意的是,并沒有沉浸式狀態(tài)欄這一概念歉胶,只有沉浸式模式和透明狀態(tài)欄的概念汛兜。
2. 實(shí)踐效果
2.1 淡化狀態(tài)欄和導(dǎo)航欄
在 Android 4.0(API level 14)及之后 的版本中,可以實(shí)現(xiàn)使?fàn)顟B(tài)欄和導(dǎo)航欄淡化的效果(Dimming the System Bars)
效果:
注意觀察狀態(tài)欄和導(dǎo)航欄的變化通今。
代碼:
/**
* 淡化狀態(tài)欄和導(dǎo)航欄
*/
private fun dimmingStatusBar() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// This example uses decor view, but you can use any visible view.
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
}
}
說明:
- 此
SYSTEM_UI_FLAG_LOW_PROFILE
標(biāo)志并不會重新分配界面中 UI 的大小粥谬,只是 StatusBar 和 NavigationBar 的相關(guān)圖標(biāo)會被弱化 - 一旦用戶觸摸 StatusBar 和 NavigationBar 相關(guān)區(qū)域,系統(tǒng)便清除掉了
SYSTEM_UI_FLAG_LOW_PROFILE
標(biāo)志 - 此
SYSTEM_UI_FLAG_LOW_PROFILE
標(biāo)志只可以在 Android 4.0(API level 14) 及之后的版本中使用 - 可以使用代碼手動清除
SYSTEM_UI_FLAG_LOW_PROFILE
標(biāo)志衡创,代碼如下所示:
private fun clearSystemUIFlag() {
window.decorView.systemUiVisibility = 0
}
2.2 隱藏狀態(tài)欄
隱藏狀態(tài)欄分為兩種情況:
- 在 Android 4.0(API level 14) 及之下的版本中隱藏狀態(tài)欄
- 在 Android 4.0(API level 14) 之上的版本中隱藏狀態(tài)欄
2.2.1 在 Android 4.0 及之下
在 Android 4.0 及之下的版本中帝嗡,可以通過設(shè)置 WindowManager
的 flag 實(shí)現(xiàn)隱藏狀態(tài)欄的效果,設(shè)置 WindowManager
的 flag 有兩種方式:
代碼:
- 通過編寫代碼設(shè)置
WindowManager
的 flag
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If the Android version is lower than Jellybean, use this call to hide
// the status bar.
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
setContentView(R.layout.activity_main);
}
...
}
-
通過設(shè)置
AndroidManifest.xml
中 Activity 的theme
設(shè)置WindowManager
的 flag<!-- style.xml --> <resources> <!-- Base application theme. --> <style name="BaseAppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <style name="FullScreenTheme" parent="BaseAppTheme"> <item name="android:windowFullscreen">true</item> </style> ... </resources>
<!-- AndroidManifest.xml --> <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lijiankun24.statusbarpractice"> <application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/BaseAppTheme" ... > <activity android:name=".activity.MainActivity" android:theme="@style/FullScreenTheme"> </activity> ... </application> </manifest>
說明:
- 通過設(shè)置 Activity 的 Theme實(shí)現(xiàn)隱藏狀態(tài)欄有如下優(yōu)點(diǎn):
- 簡單而且不易出錯(cuò)
- UI 切換更流暢璃氢,因?yàn)橄到y(tǒng)在實(shí)例化 Activity 對象之前就已經(jīng)獲得了渲染 UI 界面的相關(guān)信息
- 通過編寫代碼設(shè)置
WindowManager
的 flag 的方式更容易控制系統(tǒng) UI 的顯示和隱藏 - 現(xiàn)在市場上 Android 4.0 之下的手機(jī)已經(jīng)很少了哟玷,而且很多應(yīng)用最低版本都在 Android 4.0 之上,所以這點(diǎn)可以依情況而定
2.2.2 在 Android 4.0 之上
效果:
代碼:
private fun hideStatusBar() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN)
} else {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
}
}
說明:
- 如果只使用
SYSTEM_UI_FLAG_FULLSCREEN
標(biāo)志一也,在狀態(tài)欄區(qū)域向下滑動出現(xiàn)狀態(tài)欄的時(shí)候巢寡,內(nèi)容區(qū)域會出現(xiàn)一個(gè)擠壓的效果,如下圖所示:
hidestatusbar1.gif
- 可以使用
SYSTEM_UI_FLAG_LAYOUT_FULLSCREE
標(biāo)志椰苟,讓應(yīng)用的內(nèi)容區(qū)域顯示在狀態(tài)欄的后面抑月,還可以配合SYSTEM_UI_FLAG_LAYOUT_STABLE
標(biāo)志使用,讓布局保持穩(wěn)定 - 一旦這些標(biāo)志位被清除舆蝴,則需要重新設(shè)置讓狀態(tài)欄隱藏谦絮,可以通過監(jiān)聽狀態(tài)欄和導(dǎo)航欄的可見性题诵,判斷狀態(tài)欄和導(dǎo)航欄是否可見
- 在不同的位置設(shè)置 UI flag 是有區(qū)別的。比如层皱,如果在
onCreate()
方法中隱藏狀態(tài)欄性锭,那當(dāng)用戶按下Home
鍵的時(shí)候,狀態(tài)欄重新顯示叫胖,再打開應(yīng)用重新回到這個(gè) Activity 的時(shí)候草冈,用戶可以看到狀態(tài)欄,因?yàn)檫@時(shí)不會調(diào)用onCreate()
方法瓮增。如果在onResume()
或者onWindowFocusChanged()
就可以避免上面這種情況 - 只有當(dāng)調(diào)用
setSystemUiVisibility()
的View
是可見的時(shí)候怎棱,setSystemUiVisibility()
方法才會起作用 - 界面的切換會導(dǎo)致
setSystemUiVisibility()
的設(shè)置失效 - 如果設(shè)置內(nèi)容區(qū)域在狀態(tài)欄的背后,那當(dāng)狀態(tài)欄顯示的時(shí)候會遮擋住一部分內(nèi)容區(qū)域绷跑,為防止這種情況發(fā)生拳恋,只需要在布局文件中添加
android:fitsSystemWindows
屬性(值為true),就可以解決這種問題,效果如下圖所示:
fitsSystemWindows2.png
代碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lijiankun24.statusbarpractice.activity.FitsSystemWindowsActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:fitsSystemWindows="true"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="28sp"/>
</android.support.constraint.ConstraintLayout>
2.3 隱藏導(dǎo)航欄
效果:
代碼:
class HideNavigationbarActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
hideNavigationBar()
setContentView(R.layout.activity_hide_navigationbar)
}
private fun hideNavigationBar() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
}
}
說明:
- 按照一個(gè)通用的規(guī)則砸捏,在隱藏導(dǎo)航欄的時(shí)候诅岩,一般也需要隱藏狀態(tài)欄
- 通過這種方式隱藏導(dǎo)航欄和狀態(tài)欄之后,觸摸屏幕的任何區(qū)域带膜,導(dǎo)航欄和狀態(tài)欄都會重新出現(xiàn)且不會再消失吩谦,如果想讓導(dǎo)航欄和狀態(tài)欄消失,則需要手動重新設(shè)置 UI flag
- 如果想讓內(nèi)容區(qū)域出現(xiàn)在導(dǎo)航欄的后面膝藕,則需要配合使用
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
標(biāo)志式廷,并且最好配合使用SYSTEM_UI_FLAG_LAYOUT_STABLE
使布局保持穩(wěn)定 -
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
和SYSTEM_UI_FLAG_HIDE_NAVIGATION
可以在 Android 4.1 (API level 15)使用
2.4 沉浸式模式
效果:
SYSTEM_UI_FLAG_IMMERSIVE
:
SYSTEM_UI_FLAG_IMMERSIVE_STICKY
:
代碼:
class ImmersiveActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
hideSystemUI()
setContentView(R.layout.activity_immersive)
}
private fun hideSystemUI() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
// or View.SYSTEM_UI_FLAG_IMMERSIVE
}
}
說明:
- 真正的沉浸式模式如上面兩個(gè)圖所示,Activity 可以接收到所有的觸摸事件
- 當(dāng)使用
SYSTEM_UI_FLAG_IMMERSIVE
標(biāo)志時(shí)芭挽,用戶滑動狀態(tài)欄和導(dǎo)航欄邊緣的時(shí)候滑废,狀態(tài)欄和導(dǎo)航欄會出現(xiàn),并且不會再消失袜爪, - 當(dāng)使用
SYSTEM_UI_FLAG_IMMERSIVE_STICKY
標(biāo)志時(shí)蠕趁,用戶滑動狀態(tài)欄和導(dǎo)航欄邊緣的時(shí)候,狀態(tài)欄和導(dǎo)航欄會出現(xiàn)辛馆,但是和SYSTEM_UI_FLAG_IMMERSIVE
不同的是俺陋,過一會兒狀態(tài)欄和導(dǎo)航欄會自動消失 - 如果設(shè)置了
View.OnSystemUiVisibilityChangeListener
監(jiān)聽器,SYSTEM_UI_FLAG_IMMERSIVE
會觸發(fā)OnSystemUiVisibilityChangeListener
監(jiān)聽器昙篙,但是SYSTEM_UI_FLAG_IMMERSIVE_STICKY
不會觸發(fā)OnSystemUiVisibilityChangeListener
監(jiān)聽器
2.5 監(jiān)聽狀態(tài)欄和導(dǎo)航欄可見性
可以通過 View.OnSystemUiVisibilityChangeListener
為該 View
設(shè)置狀態(tài)欄和導(dǎo)航欄可見性的監(jiān)聽器腊状,代碼如下所示:
class RespondingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
respondingSystemUI()
setContentView(R.layout.activity_responding)
window.decorView.setOnSystemUiVisibilityChangeListener {
L.i("visibility is " + it)
}
}
private fun respondingSystemUI() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE
}
}
2.6 透明狀態(tài)欄和導(dǎo)航欄
效果:
代碼:
class TransparentNavigationbarActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
transparentingNavigationbar()
setContentView(R.layout.activity_transparent_navigationbar)
}
private fun transparentingNavigationbar() {
var uiFlag = 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
uiFlag = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
uiFlag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or uiFlag
}
window.decorView.systemUiVisibility = uiFlag
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
}
}
}
說明:
- 除了可以設(shè)置狀態(tài)欄和導(dǎo)航欄為透明,還可以設(shè)置為其他顏色
- 只可以在 Android 5.0(API level 21)及以上的 Android 版本中設(shè)置狀態(tài)欄和導(dǎo)航欄的顏色
3 總結(jié)
本文主要介紹了狀態(tài)欄苔可、導(dǎo)航欄缴挖、標(biāo)題欄、沉浸式模式和透明狀態(tài)欄的概念焚辅,以及實(shí)現(xiàn)隱藏狀態(tài)欄映屋、隱藏導(dǎo)航欄苟鸯、沉浸式模式和透明狀態(tài)欄的方式。
3.1 版本要求
實(shí)現(xiàn)的主要方式是通過設(shè)置 window.decorView.systemUiVisibility
的屬性值實(shí)現(xiàn)的棚点,但是不是所有的 Android 版本都可以實(shí)現(xiàn)上述那些效果的倔毙,實(shí)現(xiàn)上述效果的版本要求如下圖所示:
效果 | 版本要求 |
---|---|
淡化狀態(tài)欄和導(dǎo)航欄 | Version >= 14 |
隱藏狀態(tài)欄 | 全部版本 |
隱藏導(dǎo)航欄 | Version >= 16 |
沉浸式模式 | Version >= 19 |
透明狀態(tài)欄 | Version >= 21 |
3.2 相關(guān)文章
本文中涉及到的代碼在 Github 上面 StatusBarPractice
本文中只涉及到我在實(shí)現(xiàn)開屏廣告全屏過程中遇到的一些問題,但是還有其他很多相關(guān)的問題沒有涉及到乙濒,下面有一些相關(guān)文章,講的都很詳細(xì):
隨手記Android沉浸式狀態(tài)欄的踩坑之路 ---- 劉玲
管理System UI (狀態(tài)欄 + 導(dǎo)航欄) ---- ShenJC
Android 系統(tǒng)狀態(tài)欄沉浸式/透明化完整解決方案 ---- btman
Android 沉浸式 (透明) 狀態(tài)欄適配 ---- xiaoyanger
Android狀態(tài)欄微技巧卵蛉,帶你真正理解沉浸式模式 ---- 郭霖