Android沉浸式全屏讲解(状态栏、导航栏处理)

Android应用中经常会有一些要求全屏显隐状态栏导航栏的需求。通过全屏沉浸式的处理可以让应用达到更好的显示效果。下面系统的讲解一下有关全屏,隐藏状态栏导航栏,沉浸式的知识。

在Android4.1之前只能隐藏状态栏,在Android4.1以及之后Android提供了一套控制SystemUI的方式,重点放在第二部分。

在Android4.1之前隐藏状态栏

在Android4.1之前你可以通过设置WindowManager来隐藏状态栏。你可以通过编码来实现,也可以通过在manifest文件中给activity设置theme来实现。
如果你要设置activity的状态栏一直处于隐藏状态,那么在manifest设置的方式是你的首选方式(尽管也可以使用编程方式。)。实现方式如下。

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

使用manifest设置的方式有以下优点:

  1. 比编程方式更加容易维护,更不容易出错。

  2. 会有一个更加平滑的过渡,因为在实例化activity之前系统就已经拥有了呈现UI所需的信息。

另外,也可以以编程方式设置WindowsManager标志。这种方法使用户在与应用程序交互时更容易隐藏和显示状态栏。

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

当你设置WindowsManager标志时(无论是通过theme还是以编程方式),除非您的应用程序清除了这些标志,否则这些标志将保持有效。

可以使用FLAG_LAYOUT_IN_SCREEN将activity布局设置为使用相同的屏幕区域。当你启用FLAG_FILLSCREEN时,可以使用相同的屏幕区域。这将防止在状态栏隐藏和显示时调整内容的大小。

设置全屏

从这里开始才是本文的重点。

在Android4.1以及更高版本可以使用setSystemUiVisibility来控制SystemUI,为了更系统的讲解,不分别按照效果来讲,而是把用到的flag先列出来一起讲。以下flag经过互相组合能达到全屏隐藏状态栏,全屏隐藏导航栏,全屏显示状态栏导航栏等很多不同效果。

控制SystemBar相关:

  • SYSTEM_UI_FLAG_FULLSCREEN

  • SYSTEM_UI_FLAG_HIDE_NAVIGATION

  • SYSTEM_UI_FLAG_LOW_PROFILE

布局相关:

  • SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  • SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

  • SYSTEM_UI_FLAG_LAYOUT_STABLE

沉浸式相关 (4.4 引入):

  • SYSTEM_UI_IMMERSIVE

  • SYSTEM_UI_IMMERSIVE_STICKY

控制 SystemBar 相关

SYSTEM_UI_FLAG_FULLSCREEN

该属性是用来隐藏状态栏的。

View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
getSupportActionBar().hide();

通过以上代码可以实现隐藏状态栏。为了显示出全屏效果,建议同时将ActionBar隐藏掉。仅设置这一条属性,显示效果存在以下特性。

  • 当滑动system bar、点击home键menu键就会清除掉flag,状态栏会重新显示出来。

  • 并且布局也会随着状态栏的显隐进行布局调整进行重绘。

image

效果图

SYSTEM_UI_HIDE_NAVIGATION

该属性是用来隐藏导航栏的

View decorView2 = getWindow().getDecorView();
int uiOptions2 = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView2.setSystemUiVisibility(uiOptions2);
getSupportActionBar().hide();

以上代码可以实现隐藏状态栏和导航栏。
尽管可以仅仅隐藏导航栏,但是建议隐藏导航栏的时候将状态栏一并隐藏,以达到更好的沉浸效果。

  • 与隐藏状态栏不同的是点击任意布局中的任意位置都会导致导航栏导航栏重新显示出来。

  • 并且布局也会随着状态栏导航栏的显隐进行布局调整进行重绘。

image

SYSTEM_UI_LOW_PROFILE

这个属性的能力是让SystemBar在视觉上变得模糊,重要性变得更低一点。具体表现是状态栏图标仅保留电量时间关键图标,并且变暗。导航栏图标变成三个点或者变暗。这个flag使用的很少。

View decorView7 = getWindow().getDecorView();
int uiOptions7 = View.SYSTEM_UI_FLAG_LOW_PROFILE;
decorView7.setSystemUiVisibility(uiOptions7);
image

布局相关

在新的Android4.1以及之后新的SystemUI设置里,仅单独设置隐藏状态栏和导航栏的flag会导致布局重绘,为了在显隐状态栏和导航栏的时候保持布局的稳定的显示效果,就需要以下属性了。

SYSTEM_UI_FLAG_LAYOUT_STABLE

该flag的作用是保持布局稳定,避免在显隐状态栏导航栏的时候发生布局的变化。可以辅助以下SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION两个flag的使用,让应用保持全屏的情况下,布局不随状态栏导航栏显隐发生变化。也可以不配合这两个flag使用,也能达到保持布局稳定的效果,不过不能实现全屏,会留出状态栏和导航栏的位置。

View decorView8 = getWindow().getDecorView();
int uiOptions8 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView8.setSystemUiVisibility(uiOptions8);
getSupportActionBar().hide();

以上代码设置了隐藏状态栏和SYSTEM_UI_FLAG_LAYOUT_STABLE两个flag,显示效果为隐藏状态栏,布局稳定。但是布局不延伸到全屏,效果看起来还是很奇葩的。

  • 当滑动systembar、点击home键多任务键就会清除掉flag。状态栏会重新显示出来。

  • 布局不会随着状态栏的显隐进行调整变化。

image
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

可以让布局延伸到状态栏的位置。在状态栏在隐藏和显示之前切换的时候,布局稳定的显示在状态栏后面(如果显示状态栏则布局在状态栏后面,隐藏状态栏布局也不变)。

View decorView3 = getWindow().getDecorView();
int uiOptions3 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView3.setSystemUiVisibility(uiOptions3);
getSupportActionBar().hide();

以上代码显示出来的效果和上一段代码相比,布局延伸到了状态栏的位置。

  • 当滑动systembar、点击home键menu键就会清除掉flag。状态栏会重新显示出来。

  • 布局不会随着状态栏的显隐进行调整变化

image
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

可以让布局延伸到导航栏的位置。可以让导航栏在隐藏和显示之前切换的时候,布局稳定的显示在导航栏后面(如果显示导航栏则在导航栏后面,隐藏导航栏也不变)。

View decorView4 = getWindow().getDecorView();
int uiOptions4 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView4.setSystemUiVisibility(uiOptions4);
getSupportActionBar().hide();

以上代码的显示效果是状态栏和导航栏隐藏,布局延伸到了状态栏和导航栏的位置。

  • 点击任意布局就会清除掉flag。状态栏导航栏会重新显示出来。

  • 布局不会随着状态栏导航栏的显隐进行调整变化。

image

在设置了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN、SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION flag的情况,如果把状态栏导航栏颜色设置为透明,则会有透明的状态栏导航栏覆盖在布局上的效果。这也证明了即便布局状态栏导航栏出来了,布局也确实延伸到了状态栏导航栏的位置。

沉浸式相关

以上flag的组合设置中一直存在一个问题(在点击Home键、menu键等操作会导致flag被清除,导航栏一点击界面就会导致flag被清除,效果消失的问题。)。其实我们大部分情况都希望效果能够稳定的显示,而不是在简单操作之后就会消失掉。下面两个属性就是为这个问题工作的。

SYSTEM_UI_IMMERSIVE

在以上flag设置的基础上设置该属性,可以保证在点击home键、menu键时不会失去状态。但是如果手动调出systembar的时候,设置的相关flag还是会被清除掉。

View decorView5 = getWindow().getDecorView();
int uiOptions5 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_IMMERSIVE;
decorView5.setSystemUiVisibility(uiOptions5);
getSupportActionBar().hide();

以上代码的显示效果。

  • 隐藏状态栏、导航栏。

  • 布局延伸到了状态栏、导航栏的位置。

  • 布局稳定显示,不会因为状态栏的显隐来调整布局。

  • 当手动调出状态栏导航栏的时候,flag才会被清除。

image
SYSTEM_UI_IMMERSIVE_STICKY

设置这个属性后。当状态栏隐藏的时候,手动调出状态栏导航栏,显示一会儿随后就会隐藏掉。设置该属性后不会清除flag,该属性是比较常用的一种。但是离开页面肯定是会导致flag被清除掉的,以上所有flag设置都会有这种情况。

View decorView6 = getWindow().getDecorView();
int uiOptions6 = View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView6.setSystemUiVisibility(uiOptions6);
getSupportActionBar().hide();

以上代码的显示效果。

  • 隐藏状态栏、导航栏

  • 布局延伸到了状态栏、导航栏的位置。

  • 布局稳定显示,不会因为状态栏的显隐来调整布局。

  • 手动调出的状态栏导航栏会半透明显示覆盖在界面上,随后还会隐藏掉。

  • 如果离开页面还是会导致flag被清除,效果消失。

image

对于flag被清除问题,重设的位置可以放在onWindowFocusChanged中。

想要清除flag可以直接调用setSystemUiVisibility(0);

小总结

有了以上flag的总结,我们可以实现我们想要的一些效果。比如全屏隐藏状态栏导航栏、全屏显示导状态栏导航栏、全屏显示状态栏隐藏导航栏、全屏隐藏状态栏显示导航栏,甚至组合出非全屏但是隐藏状态栏导航栏等一些奇葩的显示效果。下面用一段代码展示一下全屏中的各中显示效果,也是我们项目常用的效果。

public void setFullscreen(boolean isShowStatusBar, boolean isShowNavigationBar) {
    int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

    if (!isShowStatusBar) {
        uiOptions |= View.SYSTEM_UI_FLAG_FULLSCREEN;
    }
    if (!isShowNavigationBar) {
        uiOptions |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    }
    getWindow().getDecorView().setSystemUiVisibility(uiOptions);

    //隐藏标题栏
    getSupportActionBar().hide();
    //专门设置一下状态栏导航栏背景颜色为透明,凸显效果。
    setNavigationStatusColor(Color.TRANSPARENT);
}

public void setNavigationStatusColor(int color) {
    //VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
    if (Build.VERSION.SDK_INT >= 21) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        getWindow().setNavigationBarColor(color);
        getWindow().setStatusBarColor(color);
    }
}
image
image
image
image

监听状态栏导航栏的变化

有时候我们想知道状态栏、导航栏的显隐变化,便于我们自定义一些操作。Android也提供了一个监听变化的能力。

View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener
        (new View.OnSystemUiVisibilityChangeListener() {
    @Override
    public void onSystemUiVisibilityChange(int visibility) {
        // Note that system bars will only be "visible" if none of the
        // LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
        if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
            // TODO: The system bars are visible. Make any desired
            // adjustments to your UI, such as showing the action bar or
            // other navigational controls.
        } else {
            // TODO: The system bars are NOT visible. Make any desired
            // adjustments to your UI, such as hiding the action bar or
            // other navigational controls.
        }
    }
});

上面代码和注释可以说写的很清楚了。可以在onSystemUiVisibilityChange接口中通过判断LOW_PROFILE,HIDE_NAVIGATION和FULLSCREEN的值来看显隐,0为显示,1为隐藏。

最后

如果阅读过程中发现有什么问题,欢迎交流指正。打完,收工!

image

欢迎关注~~~
欢迎关注【Funny新青年】微信公众号~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容