android沉浸状态栏+导航栏(小白条) JAVA版小白教程 (基于安卓官方教程)

沉浸状态栏+导航栏(小白条)是基本操作,但是网上的很多教程都是错误的...,安卓官方的教程对小白很不友好,所以记录下我的实现过程供大家参考。

概述

前置知识
状态栏是屏幕顶部显示时间、通知图标等等的地方
导航栏就是屏幕下方用于提供返回,返回桌面,进入多任务界面的的三个按键或着小白条
状态栏和导航栏统称为系统栏

文章分为两部分:

  1. 启用沉浸状态栏+导航栏
    沉浸系统栏的实质是:将UI内容铺满整个屏幕,应用内容显示在系统栏下方,具体看下图:
    (默认情况下,应用内容的绘制范围从顶部状态栏下方开始,延伸至底部导航栏上方。)
    image

  2. 处理视觉/操作冲突
    因为UI内容铺满整个屏幕,因此处在屏幕底部/顶部的某些组件必然被系统栏遮盖,导致用户无法看见或操作,比如下图。因此需要进行处理
    image

启用沉浸状态栏+导航栏

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);

/********************设置结束*****************************/
   }
}

设置完毕后,应该可以看到应用的内容出现在系统栏的后面:
image

注意:使用 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+才能设置

很明显我们需要做两件事情:

  1. 获取程序是不是夜间模式
    (不理解,直接看代码即可)
    通过方法getApplicationContext().getResources().getConfiguration().uiMode;即可获取程序的模式
    再和Configuration.UI_MODE_NIGHT_MASK) 取并,
    就可以通过Configuration.UI_MODE_NIGHT_YES判断是否为夜间模式

  2. 根据模式的不同,修改状态栏颜色
    修改状态栏颜色需要使用的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 或更低版本,则系统不会显示遮盖,而是提供透明的导航栏。
image

解决方法:

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内容铺满整个屏幕,因此处在屏幕底部/顶部的某些组件必然被系统栏遮盖,导致用户无法看见或操作,比如下图。
image

补:insets 区域
Insets 区域可以理解为Android系统占用的区域,比如下图的 左/右侧的后退操作区域宽 40dp,下方的主屏操作区域高 60dp:
image
在 Android 上,Insets 区域由 WindowInsets 类表示,在 AndroidX 中则使用 WindowInsetsCompat。

解决办法就是

  1. 获取Android系统占用的区域(被称为insets 区域)的大小
    通过方法: getSystemWindowInsets(),即可获取系统的insets的值

  2. 将被覆盖的组件Padding值设为该insets区域对应值大小,即可把视图从屏幕边缘向内移动到一个合适的位置,如下图。
    就是利用java代码设置padding值,具体看代码吧
    image

代码:

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;
            }

        });
/********************设置结束*****************************/
    }
}
posted @ 2023-04-09 11:11  kingwzun  阅读(5052)  评论(3编辑  收藏  举报