状态栏
在一些安卓app中,我们会发现app界面上方状态栏的字体和颜色与手机待机时不一样,难道系统会根据背景去主动变色??答案当然是否定的,系统绝对不会根据背景去主动变色。而是需要我们去手动改变系统状态栏的背景和字体颜色。之前接到一个需求就是要求系统状态栏改变,然后就在网上找了一些所谓的可以改变的文章,试了之后发现没有什么卵用。最后在多篇文章中找到了一些可用的类,然后自己组合了一下,发现可以达到需要的结果。特此记录,方便大家指导和后续使用。
首先看看对比效果:
右图中的就是改过的样式,看起来和谐了一点是吧,,,,
activity调用很简单。共两行:
- StatusBarCompat.translucentStatusBar(activity, false);
- StatusBarUtil.setImmersiveStatusBar(activity, true);
StatusBarCompat类。先判断版本号,然后根据版本号调用不同的版本去处理状态栏。全码如下:
package com.cc.systembar;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
/**
* After kitkat add fake status bar
* Created by qiu on 8/27/16.
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
class StatusBarCompatKitKat {
private static final String TAG_FAKE_STATUS_BAR_VIEW = "statusBarView";
private static final String TAG_MARGIN_ADDED = "marginAdded";
/**
* return statusBar's Height in pixels
*/
private static int getStatusBarHeight(Context context) {
int result = 0;
int resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resId > 0) {
result = context.getResources().getDimensionPixelOffset(resId);
}
return result;
}
/**
* 1. Add fake statusBarView.
* 2. set tag to statusBarView.
*/
private static View addFakeStatusBarView(Activity activity, int statusBarColor, int statusBarHeight) {
Window window = activity.getWindow();
ViewGroup mDecorView = (ViewGroup) window.getDecorView();
View mStatusBarView = new View(activity);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
layoutParams.gravity = Gravity.TOP;
mStatusBarView.setLayoutParams(layoutParams);
mStatusBarView.setBackgroundColor(statusBarColor);
mStatusBarView.setTag(TAG_FAKE_STATUS_BAR_VIEW);
mDecorView.addView(mStatusBarView);
return mStatusBarView;
}
/**
* use reserved order to remove is more quickly.
*/
private static void removeFakeStatusBarViewIfExist(Activity activity) {
Window window = activity.getWindow();
ViewGroup mDecorView = (ViewGroup) window.getDecorView();
View fakeView = mDecorView.findViewWithTag(TAG_FAKE_STATUS_BAR_VIEW);
if (fakeView != null) {
mDecorView.removeView(fakeView);
}
}
/**
* add marginTop to simulate set FitsSystemWindow true
*/
private static void addMarginTopToContentChild(View mContentChild, int statusBarHeight) {
if (mContentChild == null) {
return;
}
if (!TAG_MARGIN_ADDED.equals(mContentChild.getTag())) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mContentChild.getLayoutParams();
lp.topMargin += statusBarHeight;
mContentChild.setLayoutParams(lp);
mContentChild.setTag(TAG_MARGIN_ADDED);
}
}
/**
* remove marginTop to simulate set FitsSystemWindow false
*/
private static void removeMarginTopOfContentChild(View mContentChild, int statusBarHeight) {
if (mContentChild == null) {
return;
}
if (TAG_MARGIN_ADDED.equals(mContentChild.getTag())) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mContentChild.getLayoutParams();
lp.topMargin -= statusBarHeight;
mContentChild.setLayoutParams(lp);
mContentChild.setTag(null);
}
}
/**
* set StatusBarColor
*
* 1. set Window Flag : WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
* 2. removeFakeStatusBarViewIfExist
* 3. addFakeStatusBarView
* 4. addMarginTopToContentChild
* 5. cancel ContentChild's fitsSystemWindow
*/
static void setStatusBarColor(Activity activity, int statusColor) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup mContentView = (ViewGroup) window.findViewById(Window.ID_ANDROID_CONTENT);
View mContentChild = mContentView.getChildAt(0);
int statusBarHeight = getStatusBarHeight(activity);
removeFakeStatusBarViewIfExist(activity);
addFakeStatusBarView(activity, statusColor, statusBarHeight);
addMarginTopToContentChild(mContentChild, statusBarHeight);
if (mContentChild != null) {
ViewCompat.setFitsSystemWindows(mContentChild, false);
}
}
/**
* translucentStatusBar
*
* 1. set Window Flag : WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
* 2. removeFakeStatusBarViewIfExist
* 3. removeMarginTopOfContentChild
* 4. cancel ContentChild's fitsSystemWindow
*/
static void translucentStatusBar(Activity activity) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
View mContentChild = mContentView.getChildAt(0);
removeFakeStatusBarViewIfExist(activity);
removeMarginTopOfContentChild(mContentChild, getStatusBarHeight(activity));
if (mContentChild != null) {
ViewCompat.setFitsSystemWindows(mContentChild, false);
}
}
/**
* compat for CollapsingToolbarLayout
*
* 1. set Window Flag : WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
* 2. set FitsSystemWindows for views.
* 3. add Toolbar's height, let it layout from top, then add paddingTop to layout normal.
* 4. removeFakeStatusBarViewIfExist
* 5. removeMarginTopOfContentChild
* 6. add OnOffsetChangedListener to change statusBarView's alpha
*/
static void setStatusBarColorForCollapsingToolbar(Activity activity, final AppBarLayout appBarLayout, final CollapsingToolbarLayout collapsingToolbarLayout,
Toolbar toolbar, int statusColor) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup mContentView = (ViewGroup) window.findViewById(Window.ID_ANDROID_CONTENT);
View mContentChild = mContentView.getChildAt(0);
mContentChild.setFitsSystemWindows(false);
((View) appBarLayout.getParent()).setFitsSystemWindows(false);
appBarLayout.setFitsSystemWindows(false);
collapsingToolbarLayout.setFitsSystemWindows(false);
collapsingToolbarLayout.getChildAt(0).setFitsSystemWindows(false);
toolbar.setFitsSystemWindows(false);
if (toolbar.getTag() == null) {
CollapsingToolbarLayout.LayoutParams lp = (CollapsingToolbarLayout.LayoutParams) toolbar.getLayoutParams();
int statusBarHeight = getStatusBarHeight(activity);
lp.height += statusBarHeight;
toolbar.setLayoutParams(lp);
toolbar.setPadding(toolbar.getPaddingLeft(), toolbar.getPaddingTop() + statusBarHeight, toolbar.getPaddingRight(), toolbar.getPaddingBottom());
toolbar.setTag(true);
}
int statusBarHeight = getStatusBarHeight(activity);
removeFakeStatusBarViewIfExist(activity);
removeMarginTopOfContentChild(mContentChild, statusBarHeight);
final View statusView = addFakeStatusBarView(activity, statusColor, statusBarHeight);
CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams()).getBehavior();
if (behavior != null && behavior instanceof AppBarLayout.Behavior) {
int verticalOffset = ((AppBarLayout.Behavior) behavior).getTopAndBottomOffset();
if (Math.abs(verticalOffset) > appBarLayout.getHeight() - collapsingToolbarLayout.getScrimVisibleHeightTrigger()) {
statusView.setAlpha(1f);
} else {
statusView.setAlpha(0f);
}
} else {
statusView.setAlpha(0f);
}
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (Math.abs(verticalOffset) > appBarLayout.getHeight() - collapsingToolbarLayout.getScrimVisibleHeightTrigger()) {
if (statusView.getAlpha() == 0) {
statusView.animate().cancel();
statusView.animate().alpha(1f).setDuration(collapsingToolbarLayout.getScrimAnimationDuration()).start();
}
} else {
if (statusView.getAlpha() == 1) {
statusView.animate().cancel();
statusView.animate().alpha(0f).setDuration(collapsingToolbarLayout.getScrimAnimationDuration()).start();
}
}
}
});
}
}
注:找不到的类可在源码中复制,这里就不写了,有点多。
StatusBarUtil类。同样是根据版本号去使用不同的方式去处理。全码如下:
package com.cc.systembar;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.support.annotation.IntDef;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class StatusBarUtil {
public final static int TYPE_MIUI = 0;
public final static int TYPE_FLYME = 1;
public final static int TYPE_M = 3;//6.0
@IntDef({TYPE_MIUI,
TYPE_FLYME,
TYPE_M})
@Retention(RetentionPolicy.SOURCE)
@interface ViewType {
}
/**
* 修改状态栏颜色,支持4.4以上版本
* @param colorId 颜色
*/
public static void setStatusBarColor(Activity activity, int colorId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.setStatusBarColor(colorId);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//使用SystemBarTintManager,需要先将状态栏设置为透明
setTranslucentStatus(activity);
SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity);
systemBarTintManager.setStatusBarTintEnabled(true);//显示状态栏
systemBarTintManager.setStatusBarTintColor(colorId);//设置状态栏颜色
}
}
/**
* 设置状态栏透明
*/
@TargetApi(19)
public static void setTranslucentStatus(Activity activity) {
// 5.0以上系统状态栏透明
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
//清除透明状态栏
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
//设置状态栏颜色必须添加
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);//设置透明
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //19
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/**
* 设置沉浸式状态栏
*
* @param fontIconDark 状态栏字体和图标颜色是否为深色
*/
public static void setImmersiveStatusBar(Activity activity,boolean fontIconDark) {
setStatusBarColor(activity, Color.WHITE);
if (fontIconDark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setStatusBarFontIconDark(activity,TYPE_M);
} else if (OSUtils.isMiui()) {
setStatusBarFontIconDark(activity,TYPE_MIUI);
} else if (OSUtils.isFlyme()) {
setStatusBarFontIconDark(activity,TYPE_FLYME);
} else {//其他情况下我们将状态栏设置为灰色,就不会看不见字体
setStatusBarColor(activity,Color.LTGRAY);//灰色
}
}
}
/**
* 设置文字颜色
*/
public static void setStatusBarFontIconDark(Activity activity,@ViewType int type) {
switch (type) {
case TYPE_MIUI:
setMiuiUI(activity,true);
break;
case TYPE_M:
setCommonUI(activity);
break;
case TYPE_FLYME:
setFlymeUI(activity,true);
break;
}
}
//设置6.0的字体
public static void setCommonUI(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
//设置Flyme的字体
public static void setFlymeUI(Activity activity,boolean dark) {
try {
Window window = activity.getWindow();
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);
} catch (Exception e) {
e.printStackTrace();
}
}
//设置MIUI字体
public static void setMiuiUI(Activity activity,boolean dark) {
try {
Window window = activity.getWindow();
Class clazz = activity.getWindow().getClass();
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int 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);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
切记字体设置只有白色和黑色两种,如果要自行修改,去类中修改即可
到这为止,所有的功能都已经添加ok了。
如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。