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>
再次运行程序,效果如下图所示:
这次文字没有在状态栏了,那么问题来了,我设置 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沉浸式状态栏完全解析
总结
呼~终于写完了,写文章真心不容易,原本以为很简单,结果还是忙活了一下午时间,不过也值得的,你们的阅读,就是对我的支持。