Android:如何正确隐藏系统 UI

Posted

技术标签:

【中文标题】Android:如何正确隐藏系统 UI【英文标题】:Android: How to hide the System UI properly 【发布时间】:2016-09-19 17:16:42 【问题描述】:

我正在开发一个视频播放器应用程序,但遇到了一个问题。我有一个包含全屏按钮的自定义视频控制器,我想在用户进入全屏模式时隐藏系统 UI(导航和状态栏)。我试图做类似this 的事情,但它不能正常工作。我的应用就像下面的截图:

当我单击全屏按钮时,我将方向更改为横向并隐藏了系统 UI,但我的播放器没有全屏显示。如下图所示:

此外,当我点击屏幕时,我想显示我的视频控制器,但系统 UI 显示的是以下内容:

那么,我该如何实现这种行为呢?以下是我的全屏和隐藏/显示系统 UI 的方法:

@Override
public void toggleFullscreen() 
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mVideoContainer.getLayoutParams();
    if (!mFullscreen) 
             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
        hideSystemUI();
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        params.width = metrics.widthPixels;
        params.height = metrics.heightPixels;
        params.setMargins(0, 0, 0, 0);
     else 
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        showSystemUI();
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        params.width = metrics.widthPixels;
        params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300, metrics);
            params.setMargins(0, 0, 0, 0);
    
    mVideoContainer.setLayoutParams(params);
    mFullscreen = !mFullscreen;


private void hideSystemUI() 
    View decorView = getWindow().getDecorView();
    int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    if (Build.VERSION.SDK_INT < 16) 
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN)
     else 
        uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
    
    decorView.setSystemUiVisibility(uiOptions);


private void showSystemUI() 
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(0);

这是我的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/video_container"
    android:layout_
    android:layout_
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/videoSurfaceContainer"
        android:layout_
        android:layout_ >

        <SurfaceView
            android:id="@+id/videoSurface"
            android:layout_
            android:layout_ />
    </FrameLayout>
</LinearLayout>

【问题讨论】:

【参考方案1】:

我在每个要隐藏导航栏和状态栏的活动中都包含此内容:

    public void hideToolBr()

        // BEGIN_INCLUDE (get_current_ui_flags)
        // The UI options currently enabled are represented by a bitfield.
        // getSystemUiVisibility() gives us that bitfield.
        int uiOptions = getWindow().getDecorView().getSystemUiVisibility();
        int newUiOptions = uiOptions;
        // END_INCLUDE (get_current_ui_flags)
        // BEGIN_INCLUDE (toggle_ui_flags)
        boolean isImmersiveModeEnabled =
                ((uiOptions | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) == uiOptions);
        if (isImmersiveModeEnabled) 
            Log.i(Constants.TAG_DEF, "Turning immersive mode mode off. ");
         else 
            Log.i(Constants.TAG_DEF, "Turning immersive mode mode on.");
        

        // Navigation bar hiding:  Backwards compatible to ICS.
        if (Build.VERSION.SDK_INT >= 14) 
            newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
        

        // Status bar hiding: Backwards compatible to Jellybean
        if (Build.VERSION.SDK_INT >= 16) 
            newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
        

        // Immersive mode: Backward compatible to KitKat.
        // Note that this flag doesn't do anything by itself, it only augments the behavior
        // of HIDE_NAVIGATION and FLAG_FULLSCREEN.  For the purposes of this sample
        // all three flags are being toggled together.
        // Note that there are two immersive mode UI flags, one of which is referred to as "sticky".
        // Sticky immersive mode differs in that it makes the navigation and status bars
        // semi-transparent, and the UI flag does not get cleared when the user interacts with
        // the screen.
        if (Build.VERSION.SDK_INT >= 18) 
            newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        

        getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
        //END_INCLUDE (set_ui_flags)

    

然后在我的活动的onCreate方法中,我在setContentView()之后调用它

hideToolBr();

然后用户所要做的就是从底部向上滑动或从顶部向下滑动以调出状态栏或导航栏。谷歌将此称为“沉浸式模式”。它让开发者可以充分利用设备的屏幕空间。

取自 Google 的沉浸式模式示例,调用它会显示系统 UI:

    // This snippet shows the system bars. It does this by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() 
    mDecorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

您可以找到更多信息here。

编辑:

也将其添加到您的 styles.xml 中

    <style name="YourTheme.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:colorForeground">@color/colorPrimaryDark</item>
</style>

【讨论】:

在导航栏的位置进入全屏后仍然留下空白空间:( @OlegRyabtsev 您的布局是否与您的 xml android:layout_height="match_parent" 中的父级匹配? 我有一个 LinearLayout 作为具有 layout_ 的根布局,在其中我有一个高度为 300dp 的 FrameLayout。 FrameLayout 内部是宽高等于 match_parent 的 SurfaceView。我尝试在单击全屏按钮时动态更改 FrameLayout 的大小 @OlegRyabtsev 所以你的主要 LinearLayout 填满了屏幕,但你的 FrameLayout 没有调整以填充导航栏所在的空间。对吗? @OlegRyabtsev 您能否发布您遇到问题的布局以便我对其进行测试?【参考方案2】:

当您想要隐藏/显示系统 UI 时,只需调用这些方法即可。

private void hideSystemUI() 
    View decorView = getActivity().getWindow().getDecorView();
    int uiOptions = decorView.getSystemUiVisibility();
    int newUiOptions = uiOptions;
    newUiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
    newUiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
    newUiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    newUiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE;
    newUiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    decorView.setSystemUiVisibility(newUiOptions);


private void showSystemUI() 
    View decorView = getActivity().getWindow().getDecorView();
    int uiOptions = decorView.getSystemUiVisibility();
    int newUiOptions = uiOptions;
    newUiOptions &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
    newUiOptions &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
    newUiOptions &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    newUiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE;
    newUiOptions &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    decorView.setSystemUiVisibility(newUiOptions);

【讨论】:

我遇到了titleBar无法隐藏的问题,但遇到相同问题的任何人都可以使用getSupportActionBar().hide();getSupportActionBar().show();解决【参考方案3】:

转到 https://developer.android.com/training/system-ui/immersive

他们已经非常正确地解释了这个系统。 并在您的活动主题中添加以下样式

<style name="BlackActionTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/black</item>
        <item name="colorPrimaryDark">@color/black</item>
        <item name="colorAccent">@color/colorAccent</item>

        <item name="android:windowNoTitle">true</item>
        <item name="android:windowActionBar">false</item>
    </style>

【讨论】:

【参考方案4】:

API 版本 30 (R) 中设置 window.decorView.systemUiVisibility 已弃用。请改用window.insetsController

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) 
    window.insetsController?.let 
        it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
        window.navigationBarColor = getColor(R.color.semi_transparent)
        it.hide(WindowInsets.Type.systemBars())
    
 else 
    // see other answers

详细解释见https://medium.com/swlh/modifying-system-ui-visibility-in-android-11-e66a4128898b

【讨论】:

【参考方案5】:
**For Kotlin:** 

  private fun hideSystemUI() 
       val decorView: View = this.window.decorView
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) 
           window.setDecorFitsSystemWindows(false)
        else 
  
            val uiOptions = decorView.systemUiVisibility
            var newUiOptions = uiOptions
            newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_LOW_PROFILE
            newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_FULLSCREEN
            newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_IMMERSIVE
            newUiOptions = newUiOptions or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            decorView.systemUiVisibility = newUiOptions
        

    
 private fun showSystemUI() 
        val decorView: View = this.window.decorView
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) 
          window.setDecorFitsSystemWindows(true)
         else 
  
            val uiOptions = decorView.systemUiVisibility
            var newUiOptions = uiOptions
            newUiOptions = newUiOptions and View.SYSTEM_UI_FLAG_LOW_PROFILE.inv()
            newUiOptions = newUiOptions and View.SYSTEM_UI_FLAG_FULLSCREEN.inv()
            newUiOptions = newUiOptions and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION.inv()
            newUiOptions = newUiOptions and View.SYSTEM_UI_FLAG_IMMERSIVE.inv()
            newUiOptions = newUiOptions and View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY.inv()
            decorView.systemUiVisibility = newUiOptions
        
    

【讨论】:

systemUiVisibility 已弃用 @FerozKhan 用这个代替systemUiVisibility: if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.R) window.setDecorFitsSystemWindows(false) else window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN 【参考方案6】:

要隐藏系统 UI,我最近提出了一种干净且可扩展的方法。它支持所有 Android 版本,包括 Api 级别 31、30 及之前的版本。

object SystemBarsCompat 
    private val api: Api =
        when 
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> Api31()
            Build.VERSION.SDK_INT == Build.VERSION_CODES.R -> Api30()
            else -> Api()
        

    fun hideSystemBars(window: Window, view: View, isImmersiveStickyMode: Boolean = false) =
        api.hideSystemBars(window, view, isImmersiveStickyMode)

    fun showSystemBars(window: Window, view: View) = api.showSystemBars(window, view)

    fun areSystemBarsHidden(view: View): Boolean = api.areSystemBarsHidden(view)

    @Suppress("DEPRECATION")
    private open class Api 
        open fun hideSystemBars(window: Window, view: View, isImmersiveStickyMode: Boolean = false) 
            val flags = View.SYSTEM_UI_FLAG_FULLSCREEN or
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

            view.systemUiVisibility = if (isImmersiveStickyMode) 
                flags or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
             else 
                flags or
                    View.SYSTEM_UI_FLAG_IMMERSIVE or
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            
        

        open fun showSystemBars(window: Window, view: View) 
            view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        

        open fun areSystemBarsHidden(view: View) = view.systemUiVisibility and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION != 0
    

    @Suppress("DEPRECATION")
    @RequiresApi(Build.VERSION_CODES.R)
    private open class Api30 : Api() 

        open val defaultSystemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE

        override fun hideSystemBars(window: Window, view: View, isImmersiveStickyMode: Boolean) 
            window.setDecorFitsSystemWindows(false)
            view.windowInsetsController?.let 
                it.systemBarsBehavior =
                    if (isImmersiveStickyMode) WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
                    else defaultSystemBarsBehavior
                it.hide(WindowInsets.Type.systemBars())
            
        

        override fun showSystemBars(window: Window, view: View) 
            window.setDecorFitsSystemWindows(false)
            view.windowInsetsController?.show(WindowInsets.Type.systemBars())
        

        override fun areSystemBarsHidden(view: View) = !view.rootWindowInsets.isVisible(WindowInsets.Type.navigationBars())
    

    @RequiresApi(Build.VERSION_CODES.S)
    private class Api31 : Api30() 
        override val defaultSystemBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT
    

例如隐藏系统栏,可以从Fragment调用:

SystemBarsCompat.hideSystemBars(requireActivity().window, view)

【讨论】:

以上是关于Android:如何正确隐藏系统 UI的主要内容,如果未能解决你的问题,请参考以下文章

Android如何隐藏系统的状态栏

安卓怎么隐藏app图标

在android中正确隐藏状态栏

android如何调用显示和隐藏系统默认的输入法

如何隐藏android app图标

如何在 Android 4 中隐藏状态栏