管理系统UI之二:隐藏Status Bar (Hiding the Status Bar)

hao2010 9年前

本文将讲解如何在不同android版本中隐藏status bar,隐藏状态栏(或者是导航栏)可以让内容得到更多的展示空间,从而提供一个更加沉浸式的用户体验。

下图展示了status bar可见时app的样子:

下图展示了status bar隐藏之后app的样子,注意我们有意让actionbar也跟着不见了,我们认为,当status bar隐藏的时候,actionbar也应该是隐藏的。

一、在android4.0及以下版本中隐藏status bar

在android4.0及以下版本中,你可以通过设置WindowManager的flag来隐藏status bar。有两种方式来设置WindowManager的flag,一是在java代码中,二是在manifest中设置activity的theme。如果你的status bar是一直隐藏的话,在manifest中设置activity的theme是最好的方式。

<application  ...  android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen">  ...  </application>

设置activity的theme的优点如下:

.简单不易出错。

.UI切换更流畅。因为系统在实例化activity之前已经获得了渲染UI所需要的信息。

当然你也可以在java代码中设置WindowManager的flag:

public class MainActivity extends Activity {  @Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  // If the Android version is lower than Jellybean, use this call to hide  // the status bar.  if(Build.VERSION.SDK_INT < 16) {  getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  WindowManager.LayoutParams.FLAG_FULLSCREEN);  }  setContentView(R.layout.activity_main);  }  ...  }

当你设置了WindowManager的flag,将一直保持该flag的效果,除非你重置了flag。之所以强调这点是因为下面要讲的内容中会出现设置了某个状态但是不一定一直该保持状态。

但是在status bar切换隐藏和显示状态的同时,activity的界面会发生重新分配UI的情况,而系统在UI重绘时并没有做到视觉上的连续,看起来有点卡顿的错觉。为了防止这种情况发生,你可以使用FLAG_LAYOUT_IN_SCREEN。

注:至于如何使用,我也不知道,而且我个人认为难以起到实际效果。


二、在4.0以上版本隐藏status bar

Android 4.1 (API level 16)或者更高版本中,你可以使用setSystemUiVisibility()来隐藏status bar,使用setSystemUiVisibility()设置UI flag比使用WindowManager的flag拥有更多的控制选项。

下面是用setSystemUiVisibility隐藏statusbar的代码:

View decorView = getWindow().getDecorView();  // Hide the status bar.  int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;  decorView.setSystemUiVisibility(uiOptions);  // Remember that you should never show the action bar if the  // status bar is hidden, so hide that too if necessary.  ActionBar actionBar = getActionBar();  actionBar.hide();

一旦UI Flag被清除(比如跳转到另外的activity),你需要重新设置UI flag来隐藏system bar。参见:动态响应UI Visibility改变事件一文,这篇文章讨论了如何监听UI visibility的改变,以便在改变的同时让app的界面也做必要的改变。

以下是使用setSystemUiVisibility()方法时需要注意的地方:

.在不同的地方设置Ui flag是有区别的,如果你是在onCreate中设置UI flag隐藏system bar,当用户点击home键,system bar将重新出现,用户重新回到这个activity的时候,onCreate是不会被调用的,所以system bar仍然是可见的。因此如果你想在activity切换回来的时候仍然保持system bar的状态,最好是在onResume()或者onWindowFocusChanged()方法中设置UI flag。

.只有当调用setSystemUiVisibility()的view是可见的setSystemUiVisibility()才会起作用。

.界面的切换会导致setSystemUiVisibility()的设置被清空


三、让内容布局在status bar的背后

在 android4.1以及之后的版本,可以让activity的内容部分显示在status bar的背后,status bar不再影响到实际内容的空间摆放(以前实际内容总是在status bar下面),因此在status bar切换显示状态的时候,内容区域的大小就不会发生变化。要达到这种效果只需使用SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN标志(flag)。同时,你也有可能需要SYSTEM_UI_FLAG_LAYOUT_STABLE这个标志来帮助你的应用维持一个稳定的布局(这句话真他妈理解不能)。

 

因为内容区域在status bar之后,这个时候你就需要确保你的app不会被status bar挡住需要操作的区域,不过大多数情况下在布局文件中添加android:fitsSystemWindows属性(值为true)可以解决status bar挡住内容区域的问题,因为该设置可以调整viewGroup的padding,为系统控件预留一定的区域。对于绝大多数应用来说,这样做已经足够了。

注:既然我们反正都需要预留空间给status bar 为什么我们还需要将内容区域显示在status bar的后面呢?这一般是为了满足这种需求:

在一个显示图片的GridView中,当GridView滚动的时候,我们希望status bar背后是有内容在滚动的,但是当GridView滑到了顶端,我又希望GridView是没有被status bar挡住的。

在一些情况下,你可能需要修改默认的padding大小来获取合适的布局。为了控制内容区域的布局相对系统栏(它占据了一个叫做“内容嵌入”content insets的区域)的位置,你可以重写fitSystemWindows(Rect insets)方法。当窗口的内容嵌入区域发生变化时,fitSystemWindows()方法会被view的hierarchy调用,让View做出相应的调整适应。重写这个方法你就可以按你的意愿处理嵌入区域与应用的布局。


四、同步status bar与Action Bar的变化

在Android 4.1及以上的版本,为了防止在Action Bar隐藏和显示的时候布局发生变化,你可以使用Action Bar的overlay模式。在Overlay模式中,Activity的布局占据了所有可能的空间,好像Action Bar不存在一样,系统会在布局的上方绘制Aciton Bar。虽然这会遮盖住上方的一些布局,但是当Action Bar显示或者隐藏的时候,系统就不需要重新改变布局区域的大小。

要启用Action Bar的overlay模式,你需要创建一个继承自Action Bar主题的自定义主题,将android:windowActionBarOverlay属性设置为true。

设置SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN来让你的activity使用的屏幕区域与设置SYSTEM_UI_FLAG_FULLSCREEN时的区域相同。当你需要隐藏系统UI时,使用SYSTEM_UI_FLAG_FULLSCREEN。这个操作也同时隐藏了Action Bar(因为 windowActionBarOverlay="true"),当同时显示与隐藏ActionBar与状态栏的时候,使用一个动画来让他们相互协调。