Android 11上的沉浸式全屏

45

以前,要启用沉浸式全屏模式,您需要使用setSystemUiVisibility,例如:

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_FULLSCREEN
            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

从API 30开始,它已被弃用,那么有什么变通方法吗?

8个回答

61

为了兼容性,请使用WindowCompatWindowInsetsControllerCompat。 您需要将androidx.core的gradle依赖升级至至少1.6.0-alpha03,以便在SDK<30上支持setSystemBarsBehaviormainContainer是我的活动中的顶层ConstraintLayout

private fun hideSystemUI() {
    WindowCompat.setDecorFitsSystemWindows(window, false)
    WindowInsetsControllerCompat(window, mainContainer).let { controller ->
        controller.hide(WindowInsetsCompat.Type.systemBars())
        controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    }
}

private fun showSystemUI() {
    WindowCompat.setDecorFitsSystemWindows(window, true)
    WindowInsetsControllerCompat(window, mainContainer).show(WindowInsetsCompat.Type.systemBars())
}
您可以通过观看此YouTube视频了解更多关于WindowInsets的信息。

编辑:

我之前没有考虑到显示缺口或显示屏内的摄像头。在应用主题样式中,我添加了以下内容以在纵向模式下将我的内容展示在缺口上方(即屏幕顶部):

<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>

您可以在此链接中阅读更多信息:Display Cutout


4
我理解您的意思是控制器兼容版本应为1.5.0-alpha05 - avalancha
2
我发现在某些设备上使用这个alpha库设置systemBarsBehavior可能会出现问题。请参阅错误报告:https://issuetracker.google.com/issues/173203649#comment2 *一定要为该问题加星以获得更多关注 - James
这个答案在未来肯定是需要的。需要关注库中的错误修复。 - Chaitanya Karmarkar
1
在androidx.core版本1.6.0-alpha03的bug修复中,您可以看到此问题已得到解决。请查看其发布说明:https://developer.android.com/jetpack/androidx/releases/core。或者请参见:https://issuetracker.google.com/issues/173203649。 - Chaitanya Karmarkar
1
setDecorFitsSystemWindows(false) 真的需要吗?我没有看到任何区别,除了在 API 29 及以下版本上破坏了操作栏。 另外,有一个 WindowInsetsCompat.Type.systemBars() 可以用于所有栏。 - gmk57
显示剩余14条评论

33

以下是Kotlin答案,以防您不想使用androidx库增加应用程序大小。

private fun setFullscreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        window.attributes.layoutInDisplayCutoutMode =
            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.setDecorFitsSystemWindows(false)
        window.insetsController?.apply {
            hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
            systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        }
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        @Suppress("DEPRECATION")
        window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_IMMERSIVE
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
    }
}

在您的活动的onCreate方法中调用该函数。

ORIGINAL ANSWER:

根据文档,您应该使用WindowInsetsController

getWindow().setDecorFitsSystemWindows(false);
WindowInsetsController controller = getWindow().getInsetsController();
if (controller != null) {
    controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
    controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}

3
好的,我会尽力进行翻译。您提到您已经尝试了某个功能,但该功能需要至少 API 等级30。那么我的问题是:“对于低于等级30的 API 版本,应该实现什么?” - Mohit Yadav
2
@MohitYadav 你通常可以用if语句来实现,例如 if (Build.VERSION.SDK_INT < 30) { ... 这里是旧设备的版本 ... } else { 这里是新版本 } - Lev Leontev
1
哦,好的。我已经以同样的方式实现了,但是不确定Jetpack中是否有新的方法。谢谢。 - Mohit Yadav
有没有任何新的方法,也兼容旧版本? - Chaitanya Karmarkar
是的,你可以使其与旧版本兼容,参见我的回答:https://dev59.com/m1IG5IYBdhLWcg3w2VXM#64828028 - James
有没有办法获取 window.setDecorFitsSystemWindows(value) 设置的值?替换 (window.decorView.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0 @LeoLeontev - elementstyle

10

对于弃用问题,您可以尝试下面的代码。 对我有效。

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        final WindowInsetsController controller = getWindow().getInsetsController();

        if (controller != null)
            controller.hide(WindowInsets.Type.statusBars());
    }
    else {
        //noinspection deprecation
                getWindow().getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    }

在这段代码中出现了崩溃的应用程序。 - Gulab Sagevadiya
请再次检查。如果错误仍然存在,请分享错误信息。 - Robiul Hossain Shah

6

这个方法适用于我,混合了两个答案:

首先创建一个方法:

private void setFullScreen(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        getWindow().setDecorFitsSystemWindows(false);
        WindowInsetsController controller = getWindow().getInsetsController();
        if(controller != null) {
            controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
            controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
        }
    }
    else {
        //noinspection deprecation
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_IMMERSIVE
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    }
}

那么在创建时,只需在setContentView之后调用它,像这样:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_menu);
        setFullScreen();
    }

而且它运行得非常好:D


5

全屏显示

隐藏状态栏

private void hideSystemBars() {
    WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
    WindowInsetsControllerCompat windowInsetsController = ViewCompat.getWindowInsetsController(getWindow().getDecorView());
    if (windowInsetsController == null) {
        return;
    }
    // Configure the behavior of the hidden system bars
    windowInsetsController.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
    // Hide both the status bar and the navigation bar
    windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
}

显示状态栏

private void showSystemBars() {
    WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
    WindowInsetsControllerCompat windowInsetsController = ViewCompat.getWindowInsetsController(getWindow().getDecorView());
    if (windowInsetsController == null) {
        return;
    }
    // Configure the behavior of the hidden system bars
    windowInsetsController.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
    // Hide both the status bar and the navigation bar
    windowInsetsController.show(WindowInsetsCompat.Type.systemBars());
}

全屏模式下顶部相机剪裁

<item name="android:windowLayoutInDisplayCutoutMode" tools:ignore="NewApi">shortEdges</item>

4

我正在使用这个,完美地适用于所有版本的Android。

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            window.setDecorFitsSystemWindows(false)
            val controller = window.insetsController
            if (controller != null) {
                controller.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
                controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            }
        } else {
            // All below using to hide navigation bar
            val currentApiVersion = Build.VERSION.SDK_INT
            val flags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)

            // This work only for android 4.4+
            if (currentApiVersion >= Build.VERSION_CODES.KITKAT) {
                window.decorView.systemUiVisibility = flags
                // Code below is to handle presses of Volume up or Volume down.
                // Without this, after pressing volume buttons, the navigation bar will
                // show up and won't hide
                val decorView = window.decorView
                decorView.setOnSystemUiVisibilityChangeListener { visibility: Int ->
                    if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
                        decorView.systemUiVisibility = flags
                    }
                }
            }
        }

1
根据 Android Developers 官方 YouTube 频道的 Chris Banes 所说,使用以下代码:
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var view: View

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        view = binding.root
        setContentView(view)
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) hideSystemUI()
    }

    private fun hideSystemUI() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // Tell the window that we want to handle/fit any system windows
            WindowCompat.setDecorFitsSystemWindows(window, false)

            val controller = view.windowInsetsController

            // Hide the keyboard (IME)
            controller?.hide(WindowInsets.Type.ime())

            // Sticky Immersive is now ...
            controller?.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

            // When we want to hide the system bars
            controller?.hide(WindowInsets.Type.systemBars())

            /*val flag = WindowInsets.Type.statusBars()
            WindowInsets.Type.navigationBars()
            WindowInsets.Type.captionBar()
            window?.insetsController?.hide(flag)*/
        } else {
            //noinspection
            @Suppress("DEPRECATION")
            // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
            window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                // Set the content to appear under the system bars so that the
                // content doesn't resize when the system bars hide and show.
                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                // Hide the nav bar and status bar
                or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                or View.SYSTEM_UI_FLAG_FULLSCREEN)
        }
    }
}

这是一个示例链接 用户界面示例


你的答案已经过时了。在else块中不需要使用弃用的标志。请查看被接受的答案获取正确的解决方案。 - James

0

//你可以通过以下简单的方法来实现

public void FullScreencall() {
  if (Build.VERSION.SDK_INT < 19) {
    getWindow().getDecorView().setSystemUiVisibility(8);
  } else {
    getWindow().getDecorView().setSystemUiVisibility(InputDeviceCompat.SOURCE_TOUCHSCREEN);       
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接