Android 沉浸式状态栏的几种实现方式

Android 沉浸式状态栏的几种实现方式

沉浸式状态栏,说通俗一点,就是使状态栏的背景颜色与我们的 App 标题栏看起来一致,或者就是将状态栏隐藏,打造全屏的 App 。
沉浸式状态栏

方法1: 状态栏设置为透明

原理是将状态栏透明化,用自己的标题栏填充状态栏,因为在 android 4.4 版本后,才支持沉浸式状态栏,所以需要增加 if 判断,代码如下:


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //沉浸式状态栏,版本 >= Android 4.4
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            //透明状态栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //透明导航栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }

通过添加 flag:FLAG_TRANSLUCENT_STATUS 使状态栏透明化,这里我们顺带将系统的导航栏也处理了,哈哈。在此之前,我在 manifest 文件中设置主题的时候已将将系统标题栏隐藏了:android:theme="@android:style/Theme.Holo.Light.NoActionBar",布局就一个 TextView,我就不贴代码了。好了,运行程序,如下图所示:
透明状态栏

咦,不太对,咱们的标题栏怎么跑到状态栏的位置去了?呵呵,别着急,还有一步工作咱们没有做,就是将状态栏的空间留出来,在布局文件中的 TextView 中添加两个属性:android:clipToPadding="true"android:fitsSystemWindows="true",最终的布局文件如下:


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

        <TextView
            android:id="@+id/tv_title_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clipToPadding="true"
            android:fitsSystemWindows="true"
            android:background="@android:color/holo_blue_light"
            android:textColor="@android:color/white"
            android:gravity="center_horizontal"
            android:text="TitleBar"/>

    </LinearLayout>

再次运行程序,效果如下图所示:
透明状态栏 2

这次文字没有在状态栏了,那么问题来了,我设置 TextView 的高度是 wrap_content,最终的效果略高;最后就是文字的位置还是不对,我原本是想让它的居中位置是排除了状态栏高度的居中,效果却不理想;有解决方法的朋友可以私信我。不过这些可以在方法2中解决。

方法2: 动态加入状态栏布局(最佳方式)

实现原理就是动态获取状态栏的高度,然后将自己的标题栏控件的高度增加,填充状态栏的空间。
第一步还是如如方法1那样,添加 flag:


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //沉浸式状态栏,版本 >= Android 4.4
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            //透明状态栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //透明导航栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }

其次,添加获取状态栏高度的代码,封装成方法,代码如下:


    public int getStatusBarHeight() {
        try {
            Class<?> c = Class.forName("com.android.internal.R$dimen");
            Object obj = c.newInstance();
            Field field = c.getField("status_bar_height");
            int x = Integer.parseInt(field.get(obj).toString());
            return getResources().getDimensionPixelSize(x);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

原理就是通过反射,获取状态栏的高度。
接下来先看看我们的布局文件:


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/lib/net.xwdoor.customview"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <RelativeLayout
            android:id="@+id/rl_title_bar"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="bottom"
            android:background="@android:color/holo_blue_light">

            <TextView
                android:id="@+id/tv_title_bar"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:gravity="center"
                android:text="TitleBar"
                android:textColor="@android:color/white"/>

        </RelativeLayout>
    </LinearLayout>

基本上没有多大的变化,只是用 RelativeLayout 包裹了咱们的“标题栏” TextView,设置它的 id 叫 rl_title_bar,然后就是在代码中控制它的高度了,我也提取到方法了,代码如下:


    public void setStatusBar() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            final RelativeLayout rl_title_bar = (RelativeLayout) findViewById(R.id.rl_title_bar);
            final int statusHeight = getStatusBarHeight();
            rl_title_bar.post(new Runnable() {
                @Override
                public void run() {
                    int titleHeight = rl_title_bar.getHeight();
                    LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) rl_title_bar.getLayoutParams();
                    layoutParams.height = titleHeight + statusHeight;
                    rl_title_bar.setLayoutParams(layoutParams);
                }
            });
        }
    }

说一下这里为什么要用 post 方法进行布局,因为 setStatusBar() 方法本身是在 onCreate() 中调用的,这个时候正在进行 UI 的测量与绘制,通过调用 rl_title_bar.getHeight() 获取的高度基本上等于 0,属于无效值,但是在 post 就可以避免这样的情况。运行程序,效果图如下所示:
动态布局状态栏

 方法3: 设置系统UI元素的可见性

这里先调用 getWindow().getDecorView() 方法获取到了当前界面的 DecorView,然后调用它的 setSystemUiVisibility() 方法来设置系统 UI 元素的可见性。
具体方法是,重写 Activity 的 onWindowFocusChanged() 方法,然后加入如下逻辑即可:


    @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沉浸式状态栏完全解析

总结

呼~终于写完了,写文章真心不容易,原本以为很简单,结果还是忙活了一下午时间,不过也值得的,你们的阅读,就是对我的支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值