一提到沉浸式状态栏,第一个浮现在脑海里的词就是“碎片化”。碎片化是让 Android 开发者很头疼的问题,相信没有哪位开发者会不喜欢“write once, run anywhere”的感觉,碎片化让我们不得不耗费精力去校验代码在各个系统版本、各个机型上是否有效。因此以前我一直把沉浸式状态栏看作一块难啃的骨头,但是该面对的问题迟早还是要面对,所以,不如就此开始吧。
沉浸式状态栏的实现 方法一:通过设置 Theme 主题设置状态栏透明因为 API21 之后(也就是 android 5.0 之后)的状态栏,会默认覆盖一层半透明遮罩。且为了保持4.4以前系统正常使用,故需要三份 style 文件,即默认的values(不设置状态栏透明)、values-v19、values-v21(解决半透明遮罩问题)。
//valuse <style parent="AppTheme"> </style> // values-v19。v19 开始有 android:windowTranslucentStatus 这个属性 <style parent="Theme.AppCompat.Light.NoActionBar"> <item>true</item> <item>true</item> </style> // values-v21。5.0 以上提供了 setStatusBarColor() 方法设置状态栏颜色。 <style parent="Theme.AppCompat.Light.NoActionBar"> <item>false</item> <item>true</item> <!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色--> <item>@android:color/transparent</item> </style>由图可见,设置之后布局的内容延伸到了状态栏。但有些场景下,我们还是需要状态栏那块位置存在的(然而不存在的)。有三种解决方法:
法一:设置 fitsSystemWindows 属性引用一下官方对该属性的解释吧:
android:fitsSystemWindows
Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity.
当该属性设置 true 时,会在屏幕最上方预留出状态栏高度的 padding。
在布局的最外层设置 android:fitsSystemWindows="true" 属性。当然,也可以通过代码设置:
/** * 设置页面最外层布局 FitsSystemWindows 属性 * @param activity * @param value */ public static void setFitsSystemWindows(Activity activity, boolean value) { ViewGroup contentFrameLayout = (ViewGroup) activity.findViewById(android.R.id.content); View parentView = contentFrameLayout.getChildAt(0); if (parentView != null && Build.VERSION.SDK_INT >= 14) { parentView.setFitsSystemWindows(value); } }通过该设置保留状态栏高度的 paddingTop 后,再设置状态栏的颜色。就可以达到设想的效果。但这种方式实现有些问题,例如我们想设置状态栏为蓝色,只能通过设置最外层布局的背景为蓝色来实现,然而一旦设置后,整个布局就都变成了蓝色,只能在下方的布局内容里另外再设置白色背景,而这样就存在过度绘制了。而且设置了 fitsSystemWindows=true 属性的页面,在点击 EditText 调出 软键盘时,整个视图都会被顶上去。
法二:布局里添加占位状态栏法一:在根布局加入一个占位状态栏,这样虽然整个内容页面时顶到头的,但是因为在内容布局里添加了一个占位状态栏,所以效果与设想的一致。
<View android:id="@+id/statusBarView" android:background="@color/blue" android:layout_width="match_parent" android:layout_height="wrap_content"></View>通过反射获取状态栏高度:
/** * 利用反射获取状态栏高度 * @return */ public int getStatusBarHeight() { int result = 0; //获取状态栏高度的资源id int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = getResources().getDimensionPixelSize(resourceId); } return result; }设置占位视图高度
View statusBar = findViewById(R.id.statusBarView); ViewGroup.LayoutParams layoutParams = statusBar.getLayoutParams(); layoutParams.height = getStatusBarHeight();当然,除了从布局文件中添加这一方式之外,一样可以在代码中添加。比较推荐使用代码添加的方式,方便封装使用。
/** * 添加状态栏占位视图 * * @param activity */ private void addStatusViewWithColor(Activity activity, int color) { ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); View statusBarView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setBackgroundColor(color); contentView.addView(statusBarView, lp); }