android沉浸状态栏+导航栏(小白条) JAVA版小白教程 (基于安卓官方教程)
沉浸状态栏+导航栏(小白条)是基本操作,但是网上的很多教程都是错误的...,安卓官方的教程对小白很不友好,所以记录下我的实现过程供大家参考。
概述
前置知识
状态栏是屏幕顶部显示时间、通知图标等等的地方
导航栏就是屏幕下方用于提供返回,返回桌面,进入多任务界面的的三个按键或着小白条
状态栏和导航栏统称为系统栏
文章分为两部分:
-
启用沉浸状态栏+导航栏
沉浸系统栏的实质是:将UI内容铺满整个屏幕,应用内容显示在系统栏下方,具体看下图:
(默认情况下,应用内容的绘制范围从顶部状态栏下方开始,延伸至底部导航栏上方。)
-
处理视觉/操作冲突
因为UI内容铺满整个屏幕,因此处在屏幕底部/顶部的某些组件必然被系统栏遮盖,导致用户无法看见或操作,比如下图。因此需要进行处理
启用沉浸状态栏+导航栏
1. 请求进行全屏布局
第一步是让系统将我们应用布局扩展至系统栏后方。
需要使用的setSystemUiVisibility()设置两个标记,这两个标记可以告知系统应全屏放置应用的视图,就好像导航栏和状态栏不存在一样。
标记的含义和用法看代码吧,设置代码如下:
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraint_login);
/********************设置开始*****************************/
Window window = getWindow();
// 请求进行全屏布局
// SYSTEM_UI_FLAG_LAYOUT_STABLE
//*** Tells the system that the window wishes the content to
//*** be laid out at the most extreme scenario. See the docs for
//*** more information on the specifics
//SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
//*** Tells the system that the window wishes the content to
//*** be laid out as if the navigation bar was hidden
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
/********************设置结束*****************************/
}
}
设置完毕后,应该可以看到应用的内容出现在系统栏的后面:
注意:使用 setSystemUiVisibility() 设置其他标记时,应注意这些其他标记不会覆盖上述标记。 后面会用到
扩展:
对于其他全屏事件,您还可以设置 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,以便将状态栏移到后侧。
如果您使用的是自动处理状态栏的视图类(如 CoordinatorLayout 或 DrawerLayout),SYSTEM_UI_FLAG_LAYOUT_STABLE 和 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 标记可能已设置。
2. 更改系统栏颜色
现在应用布局已经拓展至全屏范围,因此需要同步更改一下系统栏的颜色,以便看清其后面的应用内容。
Android 10以上
在 Android 10 上,我们只需要将系统栏颜色设为完全透明即可:
1. 利用java代码
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraint_login);
Window window = getWindow();
// 请求进行全屏布局
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
/********************设置开始*****************************/
// 沉浸状态栏(给任务栏上透明的色)(Android 10 上,只需要将系统栏颜色设为完全透明即可:)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.setStatusBarColor(Color.TRANSPARENT);
}
// 沉浸导航栏(设置透明色)
window.setNavigationBarColor(Color.TRANSPARENT);
/********************设置结束*****************************/
}
}
2. 利用xml
<!-- values-v29/themes.xml -->
<style name="Theme.MyApp">
<item name="android:navigationBarColor">
@android:color/transparent
</item>
<!-- Optional, if drawing behind the status bar too -->
<item name="android:statusBarColor">
@android:color/transparent
</item>
</style>
Android 9 及更早版本(没适配)
以下是原文:
如果您决定在 Android 10 以下的设备上实现全面屏应用,则应将系统栏颜色设置为半透明,从而确保其内容可见。比如针对深色主题的系统栏,可以先试试使用 70% 不透明度的黑色进行遮盖:
<!-- values/themes.xml -->
<style name="Theme.MyApp">
<item name="android:navigationBarColor">
#B3FFFFFF
</item>
</style>
<!-- values-night/themes.xml -->
<style name="Theme.MyApp">
<item name="android:navigationBarColor">
#B3000000
</item>
</style>
您可能需要根据系统栏后面显示的内容来调整遮盖的不透明度。对于浅色主题,可以试试使用半透明浅色遮盖 (如 #B3FFFFFF)。
设置完后会有几个小问题,下面一一解决
更改状态栏字体颜色问题
如果系统是白天模式,状态栏组件颜色默认是白色,而状态栏文字颜色默认是浅色,因此需要手动将状态栏文字颜色改成深色。
注意:安卓版本6.0+才能设置
很明显我们需要做两件事情:
-
获取程序是不是夜间模式
(不理解,直接看代码即可)
通过方法getApplicationContext().getResources().getConfiguration().uiMode;
即可获取程序的模式
再和Configuration.UI_MODE_NIGHT_MASK)
取并,
就可以通过Configuration.UI_MODE_NIGHT_YES
判断是否为夜间模式 -
根据模式的不同,修改状态栏颜色
修改状态栏颜色需要使用的setSystemUiVisibility()设置标记
-
View.SYSTEM_UI_FLAG_VISIBLE
状态栏字体设置为白色
-
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
状态栏字体设置为黑色
之前说过
注意:使用 setSystemUiVisibility() 设置其他标记时,应注意这些其他标记不会覆盖上述标记。
因此需要将标记此次标记和之请求进行全屏布局
的标记合并,代码如下:
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraint_login);
Window window = getWindow();
/********************设置开始*****************************/
// 请求进行全屏布局+更改状态栏字体颜色
// 获取程序是不是夜间模式
int uiMode = getApplicationContext().getResources().getConfiguration().uiMode;
if ((uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES){
// SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_STABLE请求进行全屏布局
// SYSTEM_UI_FLAG_VISIBLE进行更改状态栏字体颜色
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_VISIBLE);//白色
} else {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//黑色
}
/********************设置结束*****************************/
// 让内容显示在系统栏的后面,也就是显示在状态栏和导航栏的后面
WindowCompat.setDecorFitsSystemWindows(window, true);
// 沉浸状态栏(给任务栏上透明的色)(Android 10 上,只需要将系统栏颜色设为完全透明即可:)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.setStatusBarColor(Color.TRANSPARENT);
}
// 沉浸导航栏(设置透明色)
window.setNavigationBarColor(Color.TRANSPARENT);
}
}
解决半透明遮盖(禁用系统栏视觉保护)
当应用声明 targetSdkVersion 为 29 以上时,系统可能会在按钮后方提供半透明遮盖,确保用户始终可以看到系统栏的内容,显示如下:
如果应用针对的是 SDK 28 或更低版本,则系统不会显示遮盖,而是提供透明的导航栏。
解决方法:
1. java代码
将下述代码放入上面代码下面即可,代码如下:
//在安卓10以上禁用系统栏视觉保护。
// 当设置了 导航栏 栏背景为透明时,NavigationBarContrastEnforced 如果为true,则系统会自动绘制一个半透明背景
// 状态栏的StatusBarContrast 效果同理,但是值默认为false,因此不用设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.setNavigationBarContrastEnforced(false);
}
2. 在主题中
将 android:enforceNavigationBarContrast 和/或 android:enforceStatusBarContrast 的值设置为 false即可。
整体代码如下
启用沉浸状态栏+导航栏的整体代码如下(我是纯java实现):
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraint_login);
Window window = getWindow();
// 请求进行全屏布局+更改状态栏字体颜色
// 获取程序是不是夜间模式
int uiMode = getApplicationContext().getResources().getConfiguration().uiMode;
if ((uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES){
// SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_STABLE请求进行全屏布局
// SYSTEM_UI_FLAG_VISIBLE进行更改状态栏字体颜色
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_VISIBLE);//白色
} else {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//黑色
}
// 让内容显示在系统栏的后面,也就是显示在状态栏和导航栏的后面
WindowCompat.setDecorFitsSystemWindows(window, true);
// 沉浸状态栏(给任务栏上透明的色)(Android 10 上,只需要将系统栏颜色设为完全透明即可:)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.setStatusBarColor(Color.TRANSPARENT);
}
// 沉浸导航栏(设置透明色)
window.setNavigationBarColor(Color.TRANSPARENT);
// 在安卓10以上禁用系统栏视觉保护。
// 当设置了 导航栏 栏背景为透明时,NavigationBarContrastEnforced 如果为true,则系统会自动绘制一个半透明背景
// 状态栏的StatusBarContrast 效果同理,但是值默认为false,因此不用设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.setNavigationBarContrastEnforced(false);
}
}
}
处理视觉/操作冲突
因为UI内容铺满整个屏幕,因此处在屏幕底部/顶部的某些组件必然被系统栏遮盖,导致用户无法看见或操作,比如下图。
补:insets 区域
Insets 区域可以理解为Android系统占用的区域,比如下图的 左/右侧的后退操作区域宽 40dp,下方的主屏操作区域高 60dp:
在 Android 上,Insets 区域由 WindowInsets 类表示,在 AndroidX 中则使用 WindowInsetsCompat。
解决办法就是
-
获取Android系统占用的区域(被称为insets 区域)的大小
通过方法:getSystemWindowInsets()
,即可获取系统的insets的值 -
将被覆盖的组件Padding值设为该insets区域对应值大小,即可把视图从屏幕边缘向内移动到一个合适的位置,如下图。
就是利用java代码设置padding值,具体看代码吧
代码:
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraint_login);
Window window = getWindow();
// 启用沉浸状态栏+导航栏(上面的代码)
// ...............
/********************设置开始*****************************/
// 处理视觉冲突
//对最底部布局设置底部Padding
LinearLayout linearBottom=findViewById(R.id.linear_bottom);
linearBottom.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@NonNull
@Override
public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
v.setPadding(v.getPaddingLeft(),v.getPaddingTop(),v.getPaddingRight(),insets.getSystemWindowInsets().bottom);
}
return insets;
}
});
//对最顶部布局设置顶部Padding
LinearLayout linear_top=findViewById(R.id.linear_top);
linear_top.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@NonNull
@Override
public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
v.setPadding(v.getPaddingLeft(),insets.getSystemWindowInsets().top,v.getPaddingRight(),v.getPaddingBottom());
}
return insets;
}
});
/********************设置结束*****************************/
}
}