如何在安卓活动中永久隐藏导航栏?

103

我想在我的活动中永久隐藏导航栏(不是整个系统UI)。 现在我正在使用这段代码

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

它隐藏了这个条,但当用户触摸屏幕时它会再次显示。有没有办法永久隐藏它,直到活动onStop()被调用?


这里有很多好的、具体的细节,都在这个官方的Google/Android链接中:启用全屏模式 - Chris Sprague
根据文档,当用户触摸屏幕时,标志会自动清除。您需要更改UI设计以始终隐藏导航栏。 - user1154390
15个回答

135

片段:

FullScreenFragment.java

HideNavigationBarComponent.java


这是适用于 Android 4.4+ 的技术。

尝试沉浸模式:https://developer.android.com/training/system-ui/immersive.html

快速片段(用于一个Activity类):

private int currentApiVersion;

@Override
@SuppressLint("NewApi")
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    currentApiVersion = android.os.Build.VERSION.SDK_INT;

    final int flags = 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;

    // This work only for android 4.4+
    if(currentApiVersion >= Build.VERSION_CODES.KITKAT)
    {

        getWindow().getDecorView().setSystemUiVisibility(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
        final View decorView = getWindow().getDecorView();
        decorView
            .setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener()
            {

                @Override
                public void onSystemUiVisibilityChange(int visibility)
                {
                    if((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0)
                    {
                        decorView.setSystemUiVisibility(flags);
                    }
                }
            });
    }

}


@SuppressLint("NewApi")
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
    super.onWindowFocusChanged(hasFocus);
    if(currentApiVersion >= Build.VERSION_CODES.KITKAT && hasFocus)
    {
        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);
    }
}

如果在按音量上或按音量下时出现导航栏显示的问题,请查看 onCreate 中添加的代码,详见setOnSystemUiVisibilityChangeListener。以下是另一个相关问题:Immersive mode navigation becomes sticky after volume press or minimise-restore

6
当用户在屏幕顶部/底部滑动时,会显示半透明的导航栏并暂时消失。那是否有可能隐藏它们? - Finder
1
@MuhammedRefaat,你的提示需要一个已经root过的设备 :( - Finder
我得到了这个异常:java.lang.NullPointerException: Attempt to invoke virtual method 'void com.android.internal.widget.ActionBarContainer.setAlpha(float)' on a null object reference at com.android.internal.app.WindowDecorActionBar.doHide(WindowDecorActionBar.java:822) - Shajeel Afzal
1
@DawidDrozd 谢谢你,但我有一个问题。我的活动根布局是RelativeLayout,并且它有一个子视图,设置了android:layout_alignParentBottom="true",导航栏消失了,但子视图没有移动到屏幕底部边缘,就好像导航栏被设置为不可见而不是消失了。你能帮忙吗? - Jack
1
如果你想利用导航栏占用的空间,你必须从视图中删除所有出现的 android:fitsSystemWindows="true" 。当Android Studio生成某些布局时,它会包含此属性。参见https://dev59.com/O53ha4cB1Zd3GeqPUXCW#42501330。 - Joe Lapp
显示剩余7条评论

77

做这个。

public void FullScreencall() {
    if(Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
        View v = this.getWindow().getDecorView();
        v.setSystemUiVisibility(View.GONE);
    } else if(Build.VERSION.SDK_INT >= 19) {
        //for new api versions.
        View decorView = getWindow().getDecorView();
        int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        decorView.setSystemUiVisibility(uiOptions);
    }
}

这个方法百分之百有效,而且你可以在较低的API版本中使用同样的方法,即使这是一个晚回答,我希望它能对其他人有所帮助。

如果你想要这个效果一直存在,只需要在你的onResume()方法中调用FullScreencall()就可以了。


2
我建议您查看https://developer.android.com/training/system-ui/immersive.html,该网页解释了您的方法只是隐藏栏,直到有交互发生并在交互完成后不久再次显示。 - Abandoned Cart
1
它不隐藏导航栏(安卓10的主页手势导航) - Kishan Solanki

17

对于想要更简单解决方案的人,我认为你可以在onStart()中只有这一行代码。

  getWindow().getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|
            View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

这被称为沉浸模式。您可以查看官方文档了解其他可能性。


我遇到了错误 (cannot find symbol View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);)protected void onStart() { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); super.onStart(); }``` - user12500983
这是我在React Native上适用的内容。 https://dev59.com/sWEi5IYBdhLWcg3wCoQp#22839594 过于复杂,而且隐藏了软件菜单栏,在后面留下了黑色的条块,阻挡了我的应用程序的其余部分。 我像这样使用它 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}public class MainActivity extends ReactActivity 下的 MainActivity.java 中,并导入了 import android.view.View; import android.os.Bundle; - user5305519

6

尝试使用此方法适用于所有版本的安卓,包括Android 30(R)

如需了解更多,请查看此类:https://github.com/fiftyonemoon/Rapid/blob/main/rapid/src/main/java/com/fom/rapid/app/UI.java

显示/隐藏所有系统栏:

 public void hideSystemUI(Window window) { //pass getWindow();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

        window.getInsetsController().hide(WindowInsets.Type.systemBars());

    } else {

        View decorView = window.getDecorView();

        int uiVisibility = decorView.getSystemUiVisibility();

        uiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
        uiVisibility |= View.SYSTEM_UI_FLAG_FULLSCREEN;
        uiVisibility |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            uiVisibility |= View.SYSTEM_UI_FLAG_IMMERSIVE;
            uiVisibility |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        }

        decorView.setSystemUiVisibility(uiVisibility);
    }
}


public void showSystemUI(Window window) { //pass getWindow();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

        window.getInsetsController().show(WindowInsets.Type.systemBars());

    } else {
        View decorView = window.getDecorView();

        int uiVisibility = decorView.getSystemUiVisibility();

        uiVisibility &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;
        uiVisibility &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
        uiVisibility &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            uiVisibility &= ~View.SYSTEM_UI_FLAG_IMMERSIVE;
            uiVisibility &= ~View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        }

        decorView.setSystemUiVisibility(uiVisibility);
    }
}

5
其他答案大多使用View中的setSystemUiVisibility()方法的标志。然而,自Android 11以来,此API已被弃用。检查我的有关修改系统UI可见性的文章以获取更多信息。该文章还解释了如何正确处理切口或如何监听可见性更改。

这里是使用新API显示/隐藏系统栏以及向后兼容的已弃用的API的代码片段:

/**
 * Hides the system bars and makes the Activity "fullscreen". If this should be the default
 * state it should be called from [Activity.onWindowFocusChanged] if hasFocus is true.
 * It is also recommended to take care of cutout areas. The default behavior is that the app shows
 * in the cutout area in portrait mode if not in fullscreen mode. This can cause "jumping" if the
 * user swipes a system bar to show it. It is recommended to set [WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER],
 * call [showBelowCutout] from [Activity.onCreate]
 * (see [Android Developers article about cutouts](https://developer.android.com/guide/topics/display-cutout#never_render_content_in_the_display_cutout_area)).
 * @see showSystemUI
 * @see addSystemUIVisibilityListener
 */
fun Activity.hideSystemUI() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.insetsController?.let {
            // Default behavior is that if navigation bar is hidden, the system will "steal" touches
            // and show it again upon user's touch. We just want the user to be able to show the
            // navigation bar by swipe, touches are handled by custom code -> change system bar behavior.
            // Alternative to deprecated SYSTEM_UI_FLAG_IMMERSIVE.
            it.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            // make navigation bar translucent (alternative to deprecated
            // WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
            // - do this already in hideSystemUI() so that the bar
            // is translucent if user swipes it up
            window.navigationBarColor = getColor(R.color.internal_black_semitransparent_light)
            // Finally, hide the system bars, alternative to View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            // and SYSTEM_UI_FLAG_FULLSCREEN.
            it.hide(WindowInsets.Type.systemBars())
        }
    } else {
        // Enables regular immersive mode.
        // For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
        // Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        @Suppress("DEPRECATION")
        window.decorView.systemUiVisibility = (
                // Do not let system steal touches for showing the navigation bar
                View.SYSTEM_UI_FLAG_IMMERSIVE
                        // Hide the nav bar and status bar
                        or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        or View.SYSTEM_UI_FLAG_FULLSCREEN
                        // Keep the app content behind the bars even if user swipes them up
                        or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
        // make navbar translucent - do this already in hideSystemUI() so that the bar
        // is translucent if user swipes it up
        @Suppress("DEPRECATION")
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
    }
}

/**
 * Shows the system bars and returns back from fullscreen.
 * @see hideSystemUI
 * @see addSystemUIVisibilityListener
 */
fun Activity.showSystemUI() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        // show app content in fullscreen, i. e. behind the bars when they are shown (alternative to
        // deprecated View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION and View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
        window.setDecorFitsSystemWindows(false)
        // finally, show the system bars
        window.insetsController?.show(WindowInsets.Type.systemBars())
    } else {
        // Shows the system bars by removing all the flags
        // except for the ones that make the content appear under the system bars.
        @Suppress("DEPRECATION")
        window.decorView.systemUiVisibility = (
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
    }
}

1
我在模拟器上使用了BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE标志尝试了您的代码,但是当我触摸屏幕时,导航栏会弹出并保持显示状态。您有什么其他建议吗?我应该设置什么才能在Android 11上获得STICKY功能? - Angel Koh
@AngelKoh 如果您想要隐藏系统UI,则必须手动调用hideSystemUI()函数。请查看我回答中提到的文章,其中包含所有这些细节。粘性模式意味着如果用户向上滑动它们,则栏将变为透明,并且应用程序也将接收到触摸事件(请参阅https://developer.android.com/training/system-ui/immersive#sticky-immersive)。 - Miloš Černilovský
1
是的,hideSystemUI() 被调用并且 UI 第一次被隐藏了。然而,之后当我触摸屏幕时,导航栏会弹出并保持显示状态。 - Angel Koh
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE的目的是防止系统窃取触摸并显示栏。因此,如果没有看到/调试真实应用程序,我不确定会发生什么。 - Miloš Černilovský
@AngelKoh,你找到解决方案了吗?正如你所提到的BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE会对点击做出反应,而不仅仅是滑动。 - Francis

4

使用:

view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

在运行Android 4+的平板电脑上,不可能隐藏系统/导航栏。
来自文档

SYSTEM_UI_FLAG_HIDE_NAVIGATION是一个新标志,可请求完全隐藏导航栏。请注意,这仅适用于某些手机使用的导航栏(它无法隐藏平板电脑上的系统栏)。


3
根据Android Developer网站的说法, 我认为你不能(就我所知)永久隐藏导航栏。 不过你可以使用一个小技巧,注意这只是一个技巧。当用户触摸屏幕时,立即隐藏导航栏。这很有趣。请查看此处
void setNavVisibility(boolean visible) {
int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | SYSTEM_UI_FLAG_LAYOUT_STABLE;
if (!visible) {
    newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
            | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}

// If we are now visible, schedule a timer for us to go invisible.
if (visible) {
    Handler h = getHandler();
    if (h != null) {
        h.removeCallbacks(mNavHider);
        if (!mMenusOpen && !mPaused) {
            // If the menus are open or play is paused, we will not auto-hide.
            h.postDelayed(mNavHider, 1500);
        }
    }
}

// Set the new desired visibility.
setSystemUiVisibility(newVis);
mTitleView.setVisibility(visible ? VISIBLE : INVISIBLE);
mPlayButton.setVisibility(visible ? VISIBLE : INVISIBLE);
mSeekView.setVisibility(visible ? VISIBLE : INVISIBLE);
}

更多有关此问题的信息,请参见以下内容...

如何在平板电脑上隐藏系统栏


2

这是我的解决方案:

首先,定义一个布尔变量来指示导航栏是否可见。

boolean navigationBarVisibility = true //because it's visible when activity is created

第二种创建方法是隐藏导航栏。
private void setNavigationBarVisibility(boolean visibility){
    if(visibility){
        View decorView = getWindow().getDecorView();
        int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
        navigationBarVisibility = false;
    }

    else
        navigationBarVisibility = true;
}

默认情况下,如果您在隐藏导航栏后点击活动,则导航栏将可见。因此,我们会获取其状态,如果它可见,我们将隐藏它。

现在将OnClickListener设置为您的视图。我使用了SurfaceView:

    playerSurface.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            setNavigationBarVisibility(navigationBarVisibility);
        }
    });

此外,当活动启动时,我们必须调用此方法。因为我们希望在开始时将其隐藏。
        setNavigationBarVisibility(navigationBarVisibility);

1
我认为以下代码可以帮助您,在setContentView()之前添加这些代码即可:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

在setContentView()后添加以下代码: getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);

1
该字段需要API级别21或更高级别。 - zackygaurav

1

这是一个安全问题: https://dev59.com/kGcs5IYBdhLWcg3wwmnA#12605313

因此,不可能通过一次调用在视图创建的开始永久隐藏平板电脑上的导航。它将被隐藏,但触摸屏幕时它会弹出。因此,只需第二次触摸屏幕即可在您的布局上引发onClickEvent。因此,您需要拦截此调用,但我尚未成功,我将在找到答案后更新我的答案。或者您已经知道答案了吗?


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