隐藏·透明-开发中实际使用到的状态栏、导航栏效果一网打尽

example img

一、前期基础知识储备

状态栏 StatusBar:顶部的一条显示时间、电量、通知等信息的 bar

导航栏 NavigationBar:底部包含 back 键、home 键以及 recent 键的 bar

实际开发中,关于两个bar的处理还是很常见的,每个应用可能采用的设置也不尽相同,这里博主就列出常用的关于两个bar的设置:

1)状态栏隐藏不可见

2)状态栏可见设为透明,随不同界面改变自己的状态

3)导航栏隐藏不可见,手指沿手机底部上滑唤出导航栏

4)导航栏可见设为透明,同时层级设为应用内容同级,以防覆盖住应用交互控件

而为了实现这些效果,需要我们掌握和系统UI显示隐藏相关的各种Flag,每种Flag适用的场景!

API方法:

getWindow().getDecorView().setSystemUiVisibility(int options)

相关Flag:

WindowManager.LayoutParams.FLAG_FULLSCREEN
隐藏状态栏

View.SYSTEM_UI_FLAG_VISIBLE API 14
默认标记

View.SYSTEM_UI_FLAG_LOW_PROFILE API 14
低调模式, 会隐藏不重要的状态栏图标

View.SYSTEM_UI_FLAG_LAYOUT_STABLE API 16
保持整个View稳定, 常和控制System UI悬浮, 隐藏的Flags共用, 使View不会因为System UI的变化而重新layout

View.SYSTEM_UI_FLAG_FULLSCREEN API 16
状态栏隐藏,效果同设置WindowManager.LayoutParams.FLAG_FULLSCREEN

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN API 16
视图延伸至状态栏区域,状态栏上浮于视图之上

View.SYSTEM_UI_FLAG_HIDE_NAVIGATION API 14
隐藏导航栏

View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION API 16
视图延伸至导航栏区域,导航栏上浮于视图之上

View.SYSTEM_UI_FLAG_IMMERSIVE API 19
沉浸模式, 隐藏状态栏和导航栏, 并且在第一次会弹泡提醒, 并且在状态栏区域滑动可以呼出状态栏(这样会系统会清楚之前设置的View.SYSTEM_UI_FLAG_FULLSCREEN或View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志)。使之生效,需要和View.SYSTEM_UI_FLAG_FULLSCREEN,View.SYSTEM_UI_FLAG_HIDE_NAVIGATION中的一个或两个同时设置。

View.SYSTEM_UI_FLAG_IMMERSIVE_STIKY API 19
与上面唯一的区别是, 呼出隐藏的状态栏后不会清除之前设置的View.SYSTEM_UI_FLAG_FULLSCREEN或View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志,在一段时间后将再次隐藏系统栏)

转自《玩转Android上透明状态栏,全屏显示以及沉浸模式(Immersive Mode)》

二、上代码,具体实现

创建一个Activity,不对状态栏和导航栏做任何处理;

public class MainActivity extends AppCompatActivity {

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

效果如图:应用内布局,为了区分在顶部和底部各放置一个颜色为“#ADD8E6”的绿色色块,加以区分系统两个bar;可以看到应用内容此时在状态栏之下,导航栏之上,位于两者之间。(多图,建议点击图片放大,会看得更清楚一点)

1)第一种处理方式:将两个Bar同时设为透明可见;

    @Override
    protected void onStart() {
        hideStatusBarNavigationBar();
//        hideBottomUIMenu();
        super.onStart();
    }
    
    /**
     * 状态栏、导航栏设为透明 这时导航栏在界面的最上层,意味着可能会盖在一些交互控件之上 这个是有问题的
     * 所以最优解有两个:①开启应用时隐藏导航栏,需要时拉出;
     * ②开启应用时将导航栏设为透明,同时保留其层级不再UI层级之上
     *
     * SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION-表示会让应用的主体内容占用系统导航栏的空间 导航栏上浮于视图之上
     * SYSTEM_UI_FLAG_HIDE_NAVIGATION-表示隐藏导航栏,但只是表面上隐藏,用手触摸一下屏幕,导航栏就会出来 效果不好
     *
     * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN-视图延伸至状态栏区域,状态栏上浮于视图之上
     * SYSTEM_UI_FLAG_FULLSCREEN-状态栏隐藏,效果同设置WindowManager.LayoutParams.FLAG_FULLSCREEN
     *
     * SYSTEM_UI_FLAG_LAYOUT_STABLE-保持整个View稳定, 常和控制System UI悬浮、隐藏的Flags共用, 使View不会因为System UI的变化而重新layout
     */
    private void hideStatusBarNavigationBar() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            window.getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                  | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                  | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
            window.setNavigationBarColor(Color.TRANSPARENT);
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }

效果如下:可以看到,两个bar都已经变为透明可见状态,色块顶到两极。

可以看到,表面上两个bar都设为了透明,OK了,但实际上,我们在使用

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

两个属性的时候,已经将导航栏和状态栏的层级改变了,它们现在位于整个界面的最上层,意味着两者会覆盖住其下面的布局,如果是一些展示效果还好,没有影响,但如果下面是交互控件呢?

如上图,我们在应用界面的最底部,放入两个按钮,此时两个按钮的位置就刚好在导航栏的下面,这时两个按钮都是无法点击状态,这种体验无疑是不好的,怎么改进呢?—— 在界面根布局中加入属性

android:fitsSystemWindows="true"

该属性不设置时默认为false,设置为true之后,会触发View的padding属性来给系统窗口留出空间,怎么理解?

绝大多数情况下,你都不需要理会status bar或者navigation bar 下面的空间,不过你需要注意不能让你的交互控件(比如Button)藏在status bar 或者 navigation bar下面。而android:fitsSystemWindows="true"的默认行为正好解决了这种情况,这个属性的作用就是通过设置View的padding,使得应用的content部分——Activity中setContentView()中传入的就是content——不会与System windows重叠。System windows 指的就是屏幕上status bar、 navigation bar等系统控件所占据的部分。

根布局增加android:fitsSystemWindows="true"后,效果如下:

2)第二种处理方式:可以动态地控制两个bar的颜色;

第二种方式建构在第一种处理的基础之上,我们改写之前的hideStatusBarNavgationBar方法:

    private void hideStatusBarNavigationBarCopy() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            window.getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.parseColor("#48D1CC"));
            window.setNavigationBarColor(Color.parseColor("#48D1CC"));
            return;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);

        }
    }

可以看到,我们为状态栏和导航栏设置了颜色,不再是之前的透明状态,然后分别写入两个点击事件中做判断,效果如下:

录屏软件没有把状态栏录下来,其实变化是一样的。

3)第三种处理:开启应用时,隐藏两个bar,手指沿手机底部或顶部滑动时唤出两个bar;

      @Override
    protected void onStart() {
//        hideStatusBarNavigationBar();
        hideBottomUIMenu();
        super.onStart();
    }  
    
    /**
     * 隐藏导航栏 状态栏
     * SYSTEM_UI_FLAG_IMMERSIVE -沉浸模式, 隐藏状态栏和导航栏并且在状态栏区域滑动可以呼出状态栏
     * (这样会系统会清楚之前设置的View.SYSTEM_UI_FLAG_FULLSCREEN或View.SYSTEM_UI_FLAG_HIDE_NAVIGATION标志)
     * SYSTEM_UI_FLAG_IMMERSIVE_STICKY- 与上面唯一的区别是, 呼出隐藏的状态栏后不会清除之前设置的
     * SYSTEM_UI_FLAG_HIDE_NAVIGATION SYSTEM_UI_FLAG_FULLSCREEN同之前的说明
     */
    protected void hideBottomUIMenu() {
        if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) {
            View v = getWindow().getDecorView();
            v.setSystemUiVisibility(View.GONE);
        } else if (Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            );
        }
    }

效果如下:可以看到效果为刚进入应用时隐藏,手指沿底部上滑时唤出导航栏。

4)第四种处理方式:真正沉浸式;

public class MainActivity extends AppCompatActivity {

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

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus && Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            decorView.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);
        }
    }

}

转自郭神《Android状态栏微技巧,带你真正理解沉浸式模式》效果和(3)中的例子一样。

可以看到之前的三个例子,对于两个bar的处理方法都是在onStart()方法中调用,而郭神的方法是在onWindowFocusChanged()中调用,两者区别在哪呢?

Activity生命周期中,onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。 如果你想要做一个Activity一加载完毕,就触发什么的话 完全可以用这个”

以上测试机为 小米MIX2S,调为经典导航键 基于Android9.0,如果有不同实现或者其他问题,欢迎留言!

————————————————————————————————————————————————————

2019年快来了,提前祝一波:读者们新年快乐!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值