前一段时间我写了一篇关于沉浸式的文章:Android实现沉浸式状态栏的那些坑
当时只是知道SystemBarTint的存在,并没有去了解它的实现效果和原理,因为搜Android沉浸式时好多都提到这个开源库,还以为很强大、很深奥,就没敢仓促的去看源码。今天把SystemBarTint拉下来一看,发现这个库仅仅只有一个类:SystemBarTintManager,全篇通读下来发现原理也是相当简单:就是在状态栏填充一个指定颜色、与状态栏等高的view
private void setupStatusBarView(Activity activity) {
Window win = activity.getWindow();
ViewGroup decorViewGroup = (ViewGroup) win.getDecorView();//拿到decorview
mStatusBarTintView = new View(context);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());
params.gravity = Gravity.TOP;
if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {
params.rightMargin = mConfig.getNavigationBarWidth();
}
mStatusBarTintView.setLayoutParams(params);
mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);
mStatusBarTintView.setVisibility(View.GONE);
// 将纯色的StatusBarTintView添加到decorview
decorViewGroup.addView(mStatusBarTintView);
}
稍微修改了一点源码,便于理解。导航栏的实现也是一样的。
这样看来,这个库仅仅是用view填充状态栏,虽然可以设置颜色、drawable,但是跟有些需求还是有出入的,所以还是可以看看另一种方案:让view延伸到状态栏,然后设置状态栏等高的padding。
可以参考http://blog.csdn.net/brian512/article/details/52096445
SystemBarTint里面对于状态栏和导航栏高度的获取方法还是可以学习一下的,那么多人用过没问题,应该算是适配不错的:
private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape";
private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";
private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
private int getStatusBarHeightHeight(Context context) {
return getInternalDimensionSize(context.getResources(), STATUS_BAR_HEIGHT_RES_NAME);
}
@TargetApi(14)
private int getActionBarHeight(Context context) {
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TypedValue tv = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics());
}
return result;
}
@TargetApi(14)
private int getNavigationBarHeight(Context context) {
Resources res = context.getResources();
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (hasNavBar(context)) {
String key;
if (mInPortrait) {
key = NAV_BAR_HEIGHT_RES_NAME;
} else {
key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;
}
return getInternalDimensionSize(res, key);
}
}
return result;
}
@TargetApi(14)
private int getNavigationBarWidth(Context context) {
Resources res = context.getResources();
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (hasNavBar(context)) {
return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);
}
}
return result;
}
@TargetApi(14)
private boolean hasNavBar(Context context) {
Resources res = context.getResources();
int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android");
if (resourceId != 0) {
boolean hasNav = res.getBoolean(resourceId);
// check override flag (see static block)
if ("1".equals(sNavBarOverride)) {
hasNav = false;
} else if ("0".equals(sNavBarOverride)) {
hasNav = true;
}
return hasNav;
} else { // fallback
return !ViewConfiguration.get(context).hasPermanentMenuKey();
}
}
private int getInternalDimensionSize(Resources res, String key) {
int result = 0;
int resourceId = res.getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId);
}
return result;
}
@SuppressLint("NewApi")
private float getSmallestWidthDp(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
} else {
// TODO this is not correct, but we don't really care pre-kitkat
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
}
float widthDp = metrics.widthPixels / metrics.density;
float heightDp = metrics.heightPixels / metrics.density;
return Math.min(widthDp, heightDp);
}