framework/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
public class SystemUIApplication extends Application implements SysUiServiceProvider {
public void startServicesIfNeeded() {
String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
startServicesIfNeeded(names);
}
private void startServicesIfNeeded(String[] services) {
mServices = new SystemUI[services.length];
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];//具体系统组件类的完整路径
Class cls = Class.forName(clsName);
mServices[i] = (SystemUI) cls.newInstance();//通过反射创建实例对象
mServices[i].start(); //调用start()启动
}
}
}
frameworks/base/packages/SystemUI/res/values/config.xml
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.Dependencyitem>
<item>com.android.systemui.util.NotificationChannelsitem>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStartitem>
<item>com.android.systemui.keyguard.KeyguardViewMediatoritem>
<item>com.android.systemui.recents.Recentsitem>
<item>com.android.systemui.volume.VolumeUIitem>
<item>com.android.systemui.stackdivider.Divideritem>
<item>com.android.systemui.SystemBarsitem>
<item>com.android.systemui.usb.StorageNotificationitem>
<item>com.android.systemui.power.PowerUIitem>
<item>com.android.systemui.media.RingtonePlayeritem>
<item>com.android.systemui.keyboard.KeyboardUIitem>
<item>com.android.systemui.pip.PipUIitem>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcheritem>
<item>@string/config_systemUIVendorServiceComponentitem>
<item>com.android.systemui.util.leak.GarbageMonitor$Serviceitem>
<item>com.android.systemui.LatencyTesteritem>
<item>com.android.systemui.globalactions.GlobalActionsComponentitem>
<item>com.android.systemui.ScreenDecorationsitem>
<item>com.android.systemui.fingerprint.FingerprintDialogImplitem>
<item>com.android.systemui.SliceBroadcastRelayHandleritem>
string-array>
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
public class SystemBars extends SystemUI {
...
@Override
public void start() {
createStatusBarFromConfig();//通过配置创建系统状态栏
}
private void createStatusBarFromConfig() {
final String clsName = mContext.getString(R.string.config_statusBarComponent);//获取系统状态栏对应类的全路径
Class<?> cls = mContext.getClassLoader().loadClass(clsName);//加载对应的类对象
mStatusBar = (SystemUI) cls.newInstance();//通过反射创建实例对象
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();//调用系统状态栏的start方法
}
frameworks/base/packages/SystemUI/res/values/config.xml
<string name="config_statusBarComponent">com.android.systemui.statusbar.phone.StatusBarstring>
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
protected StatusBarWindowView mStatusBarWindow;//状态栏窗口
protected PhoneStatusBarView mStatusBarView;//手机状态栏视图
@Override
public void start() {
createAndAddWindows(); //创建整个SystemUI视图并添加到WindowManager中
}
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
makeStatusBarView(); //创建整个SystemUI视图
}
//构建组件视图
protected void makeStatusBarView() {
final Context context = mContext;
//实例化整个SystemUI视图,包括状态栏,通知面版,锁屏
inflateStatusBarWindow(context);
...
//实例化状态栏视图
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {})
.getFragmentManager()
.beginTransaction()
//这里和id为status_bar_container的FrameLayout控件进行关联
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(), CollapsedStatusBarFragment.TAG)
.commit();
...
}
protected void inflateStatusBarWindow(Context context) {
//实例化整个SystemUI视图,包括状态栏,通知面版,锁屏
mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
}
}
frameworks/base/packages/SystemUI/res/layout/super_status_bar.xml
<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
sysui:ignoreRightInset="true"
>
<ImageView android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="match_parent" />
<ImageView android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout="@layout/car_fullscreen_user_switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<include layout="@layout/brightness_mirror" />
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
com.android.systemui.statusbar.phone.StatusBarWindowView>
以上我们简单分析了SystemUI模块状态栏组件从启动到加载状态栏布局的过程,接下来我们开始具体定制状态栏和导航栏。
frameworks/base/packages/SystemUI/res/values/config.xml
<string name="config_statusBarComponent">com.leapmotor.systemui.LeapMotorCarStatusBarstring>
在修改这个字段以后,Android系统开机之后,便会启动LeapMotorCarStatusBar 这个类。
frameworks/base/packages/SystemUI/src/com/leapmotor/systemui/LeapMotorCarStatusBar .java
public class LeapMotorCarStatusBar extends StatusBar{
//重写StatusBar的inflateStatusBarWindow方法,创建自定义状态栏视图对象。
@Override
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
FrameLayout flLeapMotor = mStatusBarWindow.findViewById(R.id.fl_leapmotor_status_bar);
LeapMotorStatusBarViewHelper statusBarHelper = new LeapMotorStatusBarViewHelper(mStatusBarWindow.getContext());
flLeapMotor.addView(statusBarHelper.getRootView());
}
}
frameworks/base/packages/SystemUI/res/layout/super_status_bar.xml
<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
sysui:ignoreRightInset="true">
<ImageView
android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:visibility="invisible"
sysui:ignoreRightInset="true" />
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="invisible" />
<ViewStub
android:id="@+id/fullscreen_user_switcher_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/car_fullscreen_user_switcher"
android:visibility="invisible" />
<include
layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<include layout="@layout/brightness_mirror" />
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:visibility="invisible"
sysui:ignoreRightInset="true" />
<FrameLayout
android:id="@+id/fl_leapmotor_status_bar"
android:layout_width="match_parent"
android:layout_height="match_parent" />
com.android.systemui.statusbar.phone.StatusBarWindowView>
以上代码和布局文件主要处理了以下几件事情:
接下来我们继续来看下自定义布局对象容器LeapMotorStatusBarViewHelper 的关键代码。
frameworks/base/packages/SystemUI/src/com/leapmotor/systemui/LeapMotorStatusBarViewHelper.java
public class LeapMotorStatusBarViewHelper {
protected Context mContext;
protected View mRootView;
public LeapMotorStatusBarViewHelper(Context mContext) {
this.mContext = mContext;
mRootView = LayoutInflater.from(mContext).inflate(R.layout.leapmotor_layout_status_bar_view, null, false);
}
public View getRootView() {
return mRootView;
}
}
通过修改LeapMotorStatusBarViewHelper和leapmotor_layout_status_bar_view.xml的内容,基本就能实现定制系统状态栏的功能了。
frameworks/base/packages/SystemUI/src/com/leapmotor/systemui/LeapMotorCarStatusBar .java
public class LeapMotorCarStatusBar extends StatusBar{
//重写StatusBar的createNavigationBar方法,创建自定义底部导航栏视图对象。
@Override
protected void createNavigationBar() {
mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
if (mShowBottom) {
mLeapMotorNaviBarViewHelper = new LeapMotorNaviBarViewHelper(mStatusBarWindow.getContext());
}
attachNavBarWindows();
}
//将导航栏添加到Window中
private void attachNavBarWindows() {
if (mShowBottom && mLeapMotorDockBarViewHelper != null) {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("LeapMotorNavigationBar");
lp.windowAnimations = 0;
mWindowManager.addView(mLeapMotorNaviBarViewHelper.getRootView(), lp);
}
}
}
继续在LeapMotorCarStatusBar中重写StatusBar的createNavigationBar方法,在该方法中创建LeapMotorNaviBarViewHelper对象,并将该对象的视图内容添加到WindowManager中,这样原生的系统导航栏就会被我们替换掉。至于自定义导航栏关键类LeapMotorNaviBarViewHelper的相关代码,请参考上面的自定义状态栏关键类LeapMotorStatusBarViewHelper的代码,这里不再继续讲述。
在成功定制系统状态栏和导航栏之后,大部分情况下,我们可能还需要将原生的下拉通知栏禁止掉,想要实现这个功能,
可以修改KeyguardViewMediator这个类的adjustStatusBarLocked方法,在该方法的最后将flags设置为StatusBarManager.DISABLE_EXPAND即可。
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
}
if (mStatusBarManager == null) {
Log.w(TAG, "Could not get status bar manager");
} else {
int flags = StatusBarManager.DISABLE_NONE;
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
}
//add by af
flags = StatusBarManager.DISABLE_EXPAND;
//add by af
mStatusBarManager.disable(flags);
}
}
状态栏和导航栏高度的最初取值都来源于frameworks/base/core/res/res/values/dimens.xml文件中的配置字段:
frameworks/base/core/res/res/values/dimens.xml
<dimen name="status_bar_height">@dimen/status_bar_height_portraitdimen>
<dimen name="status_bar_height_portrait">24dpdimen>
<dimen name="status_bar_height_landscape">@dimen/status_bar_height_portraitdimen>
<dimen name="navigation_bar_height">48dpdimen>
<dimen name="navigation_bar_height_landscape">48dpdimen>
<dimen name="navigation_bar_height_car_mode">96dpdimen>
1、状态栏和导航栏颜色的取值和PhoneWindow的generateLayout方法中的一段代码有关联:
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
int mStatusBarColor = 0;//状态栏背景颜色
int mNavigationBarColor = 0;//导航栏背景颜色
int mNavigationBarDividerColor = 0;//导航栏分割线颜色
protected ViewGroup generateLayout(DecorView decor) {
...代码省略...
//获取状态栏颜色,如果样式中不存在statusBarColor字段,则默认为黑色
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);
...代码省略...
//获取导航栏颜色,如果样式中不存在navigationBarColor字段,则默认为黑色
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
//获取导航栏分割线颜色,如果样式中不存在navigationBarDividerColor字段,则分割线默认为透明颜色
mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor, 0x00000000);
}
}
从上面的代码可以知道状态栏和导航栏的背景颜色都是从样式中获取的,如果样式中没有,则默认都为黑色背景,而导航栏分割线默认为透明颜色。
2、我们可以比照导航栏分割线,也修改状态栏和导航栏默认颜色取值为透明色:
//获取状态栏颜色,如果样式中不存在statusBarColor字段,则默认为透明颜色
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0x00000000);
...代码省略...
//获取导航栏颜色,如果样式中不存在navigationBarColor字段,则默认为透明颜色
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0x00000000);
在我们将状态栏背景和导航栏背景的默认取值0xFF000000改成0x00000000之后,当样式中不存在statusBarColor属性时,状态栏默认为透明色,当样式中不存在navigationBarColor属性时,导航栏默认为透明色。在修改完代码之后,我们还需要去除掉系统源码中的statusBarColor属性字段和navigationBarColor属性字段。
3、系统中默认的statusBarColor和navigationBarColor如下所示:
framework/base/core/res/res/values/themes.xml
<style name="Theme">
...代码省略...
- "statusBarColor"
>@color/black
- "navigationBarColor"
>@color/black
..代码省略...
style>
framework/base/core/res/res/values/themes_material.xml
<style name="Theme.Material">
...代码省略...
- "statusBarColor"
>?attr/colorPrimaryDark
- "navigationBarColor"
>@color/black
..代码省略...
style>
<style name="Theme.Material.Light" parent="Theme.Light">
...代码省略...
- "statusBarColor"
>?attr/colorPrimaryDark
- "navigationBarColor"
>@color/black
..代码省略...
style>
从上面的代码可以知道:
1)如果应用的样式继承自Theme,则状态栏和导航栏背景的默认颜色为黑色的,
2)如果应用的样式继承自Theme.Material或Theme.Material.Light,则状态栏背景的默认颜色取自colorPrimaryDark属性,导航栏背景的默认颜色为黑色。
4、如果我们想让前面第2步在PhoneWindow中修改的代码
//获取状态栏颜色,如果样式中不存在statusBarColor字段,则默认为透明颜色
mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0x00000000);
...代码省略...
//获取导航栏颜色,如果样式中不存在navigationBarColor字段,则默认为透明颜色
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0x00000000);
生效,只需要将第3步样式中的statusBarColor和navigationBarColor属性去除或者设置为transparent即可。
framework/base/core/res/res/values/themes.xml
<style name="Theme">
...代码省略...
- "statusBarColor"
>@color/transparent
- "navigationBarColor"
>@color/transparent
..代码省略...
style>
framework/base/core/res/res/values/themes_material.xml
<style name="Theme.Material">
...代码省略...
- "statusBarColor"
>@color/transparent
- "navigationBarColor"
>@color/transparent
..代码省略...
style>
<style name="Theme.Material.Light" parent="Theme.Light">
...代码省略...
- "statusBarColor"
>@color/transparent
- "navigationBarColor"
>@color/transparent
..代码省略...
style>
这样只要应用所使用的样式中没有添加自定义的statusBarColor和navigationBarColor属性字段,状态栏和导航栏背景的默认颜色就会变成透明颜色。