一、什么是沉浸式状态栏
首先请让我们看看没有使用沉浸式的效果:
上面这张图是没有使用沉浸式的美团外卖,我们可以看到最上面的黑色的那一条就是状态栏,大部分android默认是黑色底,白色字。
好了,下面让我祭出使用了沉浸式以后的效果图:
说回来吧,状态栏(系统)跟我们的app的标题栏的颜色融为一体,非常美观大方得体。而这样就是我今天要介绍的效果。
这里需要注意的的是:ISO很早以前原生就支持了沉浸式状态栏,但是android要到API 19以上才有了支持,也就是说,API 19以下的手机是不能兼容的。如果你们的项目经理叫你们去适配API 19以下的手机,那你就可以告诉他唯一的解决的办法就是要用户去买一台API 19以上的手机,哈哈!
为了能够和ISO尽量统一风格,为了更加美观大方,我们还是有必要学习一下沉浸式的实现方法。
二、沉浸式状态栏的实现方式
方式1:布局的内容不进入状态栏
这种方法主要是通过GitHub上面的一个开源库实现,下面让我原汁原味贴出项目的源码:
//需要引入的类库 compile 'com.readystatesoftware.systembartint:systembartint:1.0.4' import android.annotation.TargetApi; import android.app.Activity; import android.os.Build; import android.view.View; import android.view.Window; import android.view.WindowManager; import com.readystatesoftware.systembartint.SystemBarTintManager; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 设置状态栏颜色的工具类 */ public class TintBarUtils { /** * 设置activity的状态栏颜色,注意需要在activity的布局当中添加属性:android:fitsSystemWindows="true" * * @param activity * @param statusBarTintColor */ public static void setStatusBarTintColor(Activity activity, int statusBarTintColor) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { setTranslucentStatus(true, activity); SystemBarTintManager mTintManager = new SystemBarTintManager(activity); mTintManager.setStatusBarTintEnabled(true); mTintManager.setNavigationBarTintEnabled(true); mTintManager.setTintColor(statusBarTintColor); setStatusBarTextStyle(activity, true); } } public static void setStatusBarTextStyle(Activity activity, boolean lightMode) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (lightMode) { activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } } else { MIUISetStatusBarLightMode(activity.getWindow(), lightMode); FlymeSetStatusBarLightMode(activity.getWindow(), lightMode); } } @TargetApi(19) private static void setTranslucentStatus(boolean on, Activity activity) { Window win = activity.getWindow(); WindowManager.LayoutParams winParams = win.getAttributes(); final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; if (on) { winParams.flags |= bits; } else { winParams.flags &= ~bits; } win.setAttributes(winParams); } @TargetApi(19) public static void hideStatusBar(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { setTranslucentStatus(false, activity); SystemBarTintManager mTintManager = new SystemBarTintManager(activity); mTintManager.setStatusBarTintEnabled(false); mTintManager.setNavigationBarTintEnabled(false); Window win = activity.getWindow(); WindowManager.LayoutParams winParams = win.getAttributes(); final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN; winParams.flags &= ~bits; win.setAttributes(winParams); } } /** * 设置状态栏图标为深色和魅族特定的文字风格 * 可以用来判断是否为Flyme用户 * @param window 需要设置的窗口 * @param dark 是否把状态栏字体及图标颜色设置为深色 * @return boolean 成功执行返回true * */ public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) { boolean result = false; if (window != null) { try { WindowManager.LayoutParams lp = window.getAttributes(); Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); darkFlag.setAccessible(true); meizuFlags.setAccessible(true); int bit = darkFlag.getInt(null); int value = meizuFlags.getInt(lp); if (dark) { value |= bit; } else { value &= ~bit; } meizuFlags.setInt(lp, value); window.setAttributes(lp); result = true; } catch (Exception e) { e.printStackTrace(); } } return result; } /** * 设置状态栏字体图标为深色,需要MIUIV6以上 * * @param window 需要设置的窗口 * @param dark 是否把状态栏字体及图标颜色设置为深色 * @return boolean 成功执行返回true */ public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) { boolean result = false; if (window != null) { Class clazz = window.getClass(); try { int darkModeFlag = 0; Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); darkModeFlag = field.getInt(layoutParams); Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); if (dark) { extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体 }else{ extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体 } result=true; }catch (Exception e){ } } return result; } } } catch (Exception e) { e.printStackTrace(); } } return result; } }
这里需要特别强调一点就是activity布局文件中的跟布局必须有下面这个属性,否则我们的设置是没有效果的:
android:fitsSystemWindows="true"
我们要关注的核心方法是setStatusBarTintColor,这个方法需要传入一个Activity对象,以及颜色值,即可设置状态栏的颜色。需要注意的是这个方法需要判断当前的API版本是不是大于19才能使用。至于具体的实现,大家可以仔细看源码。这里只强调如何快速使用到你的项目当中。
但是这里有一个尴尬的问题就是:当我们的APP标题栏是白色的时候,例如上面的第二张图,如果我们设置了白色的状态栏,那么状态辣女白色的文字将会看不到。解决的办法比较棘手,我当时也是懵逼了好久,在项目经理的重压之下还是找到了解决方案。在android M也就是API23以上,google官方提供了设置状态栏文字风格的方法:
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
为了向下兼容,小米和魅族两个品牌由于深度定制过android的ROM,都提供了通过反射的方式是改变状态栏的文字颜色。他们分别是:
MIUISetStatusBarLightMode和FlymeSetStatusBarLightMode,通过传入的参数dark为true就可改变状态栏文字颜色,至于怎么改变,大家可以亲自尝试一下,这里不再啰嗦。
方式2:布局的内容进入状态栏
如上面第三张图所示,我们的contentView进入了状态栏,这又是怎么实现的呢?其实很简单,只要把我们的布局延伸到状态栏,并且添加一个高度为状态栏高度的View(status_bar_top)或者上Padding即可。
protected int statusBarHeight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); immerseLayout(); } protected void immerseLayout() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); statusBarHeight = getStatusBarHeight(this.getBaseContext()); viewStatusBarTop = findViewById(R.id.status_bar_top); RelativeLayout.LayoutParams params2 = (RelativeLayout.LayoutParams) viewStatusBarTop.getLayoutParams(); params2.height = statusBarHeight; viewStatusBarTop.setLayoutParams(params2); viewStatusBarTop.setVisibility(View.VISIBLE); } } public int getStatusBarHeight(Context context) { if (statusBarHeight != 0) return statusBarHeight; int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusBarHeight = context.getResources().getDimensionPixelSize(resourceId); } return statusBarHeight; }
下面给出这个activity的布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="false"> <View android:id="@+id/status_bar_top" android:layout_width="match_parent" android:layout_height="0dp" android:layout_alignParentTop="true" android:background="@drawable/status_bar" android:visibility="gone" /> 。。。这里是我们的布局 </RelativeLayout>