Android 11 NavigationBar && Status Bar 如果改变背景颜色
SystemUI的导航栏和状态栏的背景是大部分是根据当前应用的主题显示的,状态有 黑,白,透明,半透明等.
需求:要求背景不跟随栈顶应用主题变化,始终固定成一个颜色!
/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
//NavigationBarView初始化
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
...
mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class));
...
}
/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java(导航栏背景过渡)
//NavigationBarTransitions 继承了BarTransitions
public NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue) {
super(view, R.drawable.nav_background);//调用了父类的构造方法,并且传一个背景颜色
mView = view;
mLightTransitionsController = new LightBarTransitionsController(
view.getContext(), this, commandQueue);
...
}
/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
public BarTransitions(View view, int gradientResourceId) {
mTag = "BarTransitions." + view.getClass().getSimpleName();
mView = view;
mBarBackground = new BarBackgroundDrawable(mView.getContext(), gradientResourceId);
mView.setBackground(mBarBackground);//不管布局文件里面怎么改变背景颜色,最后的颜色都会在这里被重新设置!
}
//BarBackgroundDrawable 是BarTransitions的一个内部类,所以改变mBarBackground,就能改变背景颜色!
//BarBackgroundDrawable里面有个onDraw的函数,可以通过改mMode的数值,改颜色
@Override
public void draw(Canvas canvas) {
int targetGradientAlpha = 0, targetColor = 0;
if (mMode == MODE_WARNING) {
targetColor = mWarning;
} else if (mMode == MODE_TRANSLUCENT) {
targetColor = mSemiTransparent;
} else if (mMode == MODE_SEMI_TRANSPARENT) {
targetColor = mSemiTransparent;
} else if (mMode == MODE_TRANSPARENT || mMode == MODE_LIGHTS_OUT_TRANSPARENT) {
targetColor = mTransparent;
} else {
targetColor = mOpaque;
}
...
/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java(状态栏背景过渡)
//跟上面的流程差不多
//Android 11 修改底部导航栏显示半透明背景/透明背景 部分应用没看到效果,Settings可以
/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
final Context context = getContext();
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
//mForcedStatusBarColor 强制状态栏的颜色
if (!mForcedStatusBarColor) {
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
}
//mForcedNavigationBarColor 强制导航栏的颜色
if (!mForcedNavigationBarColor) {
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,
0x00000000);
}
if (!targetPreQ) {
mEnsureStatusBarContrastWhenTransparent = a.getBoolean(
R.styleable.Window_enforceStatusBarContrast, false);
- mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(
R.styleable.Window_enforceNavigationBarContrast, true);
+ mEnsureNavigationBarContrastWhenTransparent = a.getBoolean(
R.styleable.Window_enforceNavigationBarContrast, false);
}
...
//系统关于NavigationBarColor的函数
@Override
public int getNavigationBarColor() {
return mNavigationBarColor;
}
@Override
public void setNavigationBarColor(int color) {
mNavigationBarColor = color;
mForcedNavigationBarColor = true;
if (mDecor != null) {
mDecor.updateColorViews(null, false /* animate */);
}
final WindowControllerCallback callback = getWindowControllerCallback();
if (callback != null) {
getWindowControllerCallback().updateNavigationBarColor(color);
}
}
//app层窗口等级图
------Activity----------------------|
----PhoneWindow-------------------|
--DecorView---------------------|
-content-----------------------|
ViewGroup->view(各种控件) |
------------------------------------|
DecorView:每个Activity对应一个DecorView。DecorView是真正的顶层View,是最外层的view。
其inflate(填充) PhoneWidow传过来的layout后,将其addView作为自己的第0个类。
DecorView维护了一个窗口的基本结构,包括主内容区域、titlebar区域等
//系统的某些apk 调用setNavigationBarColor()设置导航栏颜色
./packages/apps/DocumentsUI/src/com/android/documentsui/BaseActivity.java:438:
getWindow().setNavigationBarColor(Color.TRANSPARENT);
./packages/apps/DocumentsUI/src/com/android/documentsui/BaseActivity.java:441:
getWindow().setNavigationBarColor(getColor(R.color.nav_bar_translucent));
利用上面这一点,在framework层启动app的节点,都调用上述函数(setNavigationBarColor),是导航栏变为透明/半透明
当进入一个Activity后,会执行attach()→onCreate()
{
setContentView(R.layout.activity_main);//设置布局,加载布局文件
}
→onStart()→onRestoreInstanceState()→onPostCreate()→onResume()
{
//onResume执行完之后,用户可以看见界面(View)
}...
ActivityThread extends ClientTransactionHandler,ActivityThread的handler调用handleLaunchActivity处理启动Activity的消息
ActivityThread::performLaunchActivity和ActivityThread::handleResumeActivity,
performLaunchActivity会调用Activity的onCreate,onStart,onResotreInstanceState方法,
handleResumeActivity会调用Activity的onResume方法.
./frameworks/base/core/java/android/app/ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
//注:执行onResum
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
if (r == null) {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
...
final Activity a = r.activity;
if (localLOGV) {
Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity
+ ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
}
final int forwardBit = isForward
? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//这里是处理Activity的View显示的
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//add text
//r.window.setNavigationBarColor(Color.TRANSPARENT);//透明/半透明
r.window.setNavigationBarColor(Color.BLACK);
//add text
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
...