Android12 CarSystemBar源码分析<一> 从创
前言
Android12内置了dagger2框架,会通过Dagger2启动CarSystemBar车载状态栏和底部栏对象。
一、从CarSystemBar的start方法到调用buildNavBarWindows方法构建NavigationBarFrame视图:
1、CarSystemBar的构造方法如下所示:
package/app/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBar.java
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
...代码省略...
@Inject
public CarSystemBar(Context context,
CarSystemBarController carSystemBarController,
// TODO(b/156052638): Should not need to inject LightBarController
LightBarController lightBarController,
DarkIconDispatcher darkIconDispatcher,
WindowManager windowManager,
CarDeviceProvisionedController deviceProvisionedController,
CommandQueue commandQueue,
AutoHideController autoHideController,
ButtonSelectionStateListener buttonSelectionStateListener,
@Main DelayableExecutor mainExecutor,
@UiBackground Executor uiBgExecutor,
IStatusBarService barService,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
Lazy<PhoneStatusBarPolicy> iconPolicyLazy,
StatusBarSignalPolicy signalPolicy,
HvacController hvacController,
SystemBarConfigs systemBarConfigs
) {
super(context);
mCarSystemBarController = carSystemBarController;
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
mWindowManager = windowManager;
mCarDeviceProvisionedController = deviceProvisionedController;
mCommandQueue = commandQueue;
mAutoHideController = autoHideController;
mButtonSelectionStateListener = buttonSelectionStateListener;
mExecutor = mainExecutor;
mUiBgExecutor = uiBgExecutor;
mBarService = barService;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
mIconPolicyLazy = iconPolicyLazy;
mHvacController = hvacController;
mSystemBarConfigs = systemBarConfigs;
mSignalPolicy = signalPolicy;
mDisplayId = context.getDisplayId();
}
...代码省略...
}
dagger2框架会通过CarSystemBar的构造方法上的注解@Inject创建CarSystemBar实例对象,
紧接着CarSystemBar的start方法会被调用。
2、start方法中有一个关键的调用createSystemBar,该方法会构建状态栏视图和导航栏视图:
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
...代码省略...
public void start() {
...代码省略...
createSystemBar(result);
...代码省略...
}
...代码省略...
}
3、createSystemBar方法如下所示:
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
...代码省略...
private void createSystemBar(RegisterStatusBarResult result) {
buildNavBarWindows();//构建视图对象容器
buildNavBarContent();
attachNavBarWindows();
...代码省略...
}
...代码省略...
}
首先调用buildNavBarWindows构建顶部栏、底部栏、左侧栏、右侧栏这四种导航栏视图对象,然后再调用buildNavBarContent构建每种导航栏所对应的具体视图内容,最后会调用attachNavBarWindows将状态栏和导航栏视图添加到Window中。
4、先来看下buildNavBarWindows方法:
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
private final CarSystemBarController mCarSystemBarController;//车载系统栏控制器
...代码省略...
//视图
private ViewGroup mTopSystemBarWindow;//顶部栏视图容器
private ViewGroup mBottomSystemBarWindow;//底部栏视图容器
private ViewGroup mLeftSystemBarWindow;//左侧栏视图容器
private ViewGroup mRightSystemBarWindow;//右侧栏视图容器
...代码省略...
private void buildNavBarWindows() {
mTopSystemBarWindow = mCarSystemBarController.getTopWindow();//获取顶部栏视图容器
mBottomSystemBarWindow = mCarSystemBarController.getBottomWindow();//获取底部栏视图容器
mLeftSystemBarWindow = mCarSystemBarController.getLeftWindow();//获取左侧栏视图容器
mRightSystemBarWindow = mCarSystemBarController.getRightWindow();//获取右侧栏视图容器
}
}
buildNavBarWindows方法会调用CarSystemBarController的get_Window方法对CarSystemBar中存在的对四个视图对象视图容器mTopSystemBarWindow、mBottomSystemBarWindow、mLeftSystemBarWindow、mRightSystemBarWindow进行赋值。
5、CarSystemBarController关于get_Window的方法如下:
package/app/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBarController.java
public class CarSystemBarController {
...代码省略...
private final CarSystemBarViewFactory mCarSystemBarViewFactory;
...代码省略...
@Nullable
public ViewGroup getTopWindow() {
return mShowTop ? mCarSystemBarViewFactory.getTopWindow() : null;
}
@Nullable
public ViewGroup getBottomWindow() {
return mShowBottom ? mCarSystemBarViewFactory.getBottomWindow() : null;
}
@Nullable
public ViewGroup getLeftWindow() {
return mShowLeft ? mCarSystemBarViewFactory.getLeftWindow() : null;
}
@Nullable
public ViewGroup getRightWindow() {
return mShowRight ? mCarSystemBarViewFactory.getRightWindow() : null;
}
...代码省略...
}
可见CarSystemBarController的get_Window方法会进一步调用CarSystemBarViewFactory的get_Window方法。
6、CarSystemBarViewFactory关于get_Window的关键代码如下所示:
package/app/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBarViewFactory.java
@SysUISingleton
public class CarSystemBarViewFactory {
...代码省略...
private final ArrayMap<Type, ViewGroup> mCachedContainerMap = new ArrayMap<>();
...代码省略...
public CarSystemBarView getTopBar(boolean isSetUp) {
return getBar(isSetUp, Type.TOP, Type.TOP_UNPROVISIONED);
}
public CarSystemBarView getBottomBar(boolean isSetUp) {
return getBar(isSetUp, Type.BOTTOM, Type.BOTTOM_UNPROVISIONED);
}
public CarSystemBarView getLeftBar(boolean isSetUp) {
return getBar(isSetUp, Type.LEFT, Type.LEFT_UNPROVISIONED);
}
public CarSystemBarView getRightBar(boolean isSetUp) {
return getBar(isSetUp, Type.RIGHT, Type.RIGHT_UNPROVISIONED);
}
private ViewGroup getWindowCached(Type type) {
if (mCachedContainerMap.containsKey(type)) {
return mCachedContainerMap.get(type);
}
ViewGroup window = (ViewGroup) View.inflate(mContext,
R.layout.navigation_bar_window, /* root= */ null);
mCachedContainerMap.put(type, window);
return mCachedContainerMap.get(type);
}
...代码省略...
CarSystemBarViewFactory的get_Bar方法会进一步调用getWindowCached方法,而getWindowCached会先判断类型为ArrayMap<Type, ViewGroup>的缓存mCachedContainerMap中是否存在对应的CarSystemBarView视图,如果存在直接返回;否则会调用View的inflate方法将R.layout.navigation_bar_window布局文件构建成相应的视图对象,然后存储到mCachedContainerMap中,并返回该视图对象。
7、来看下navigation_bar_window.xml这个布局文件。
framework/base/package/SystemUI/res/layout/navigation_bar_window.xml
<com.android.systemui.navigationbar.NavigationBarFrame
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_bar_frame"
android:theme="@style/Theme.SystemUI"
android:layout_height="match_parent"
android:layout_width="match_parent">
</com.android.systemui.navigationbar.NavigationBarFrame>
就是一个很普通的布局文件,只有一个自定义控件NavigationBarFrame。
framework/base/package/SystemUI/src/com/android/systemui/navigationbar/NavigationBarFrame.java
public class NavigationBarFrame extends FrameLayout {
private DeadZone mDeadZone = null;
public NavigationBarFrame(@NonNull Context context) {
super(context);
}
public NavigationBarFrame(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setDeadZone(@NonNull DeadZone deadZone) {
mDeadZone = deadZone;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == ACTION_OUTSIDE) {
if (mDeadZone != null) {
return mDeadZone.onTouchEvent(event);
}
}
return super.dispatchTouchEvent(event);
}
}
NavigationBarFrame就是一个继承自FrameLayout的自定义控件,单词Frame有边框的意思,这也进一步说明NavigationBarFrame这个控件就是NavigationBar的边框的意思,在前面第4步提到的CarSystemBar调用buildNavBarWindows方法所构建的四个视图容器对象mTopSystemBarWindow、mBottomSystemBarWindow、mLeftSystemBarWindow、mRightSystemBarWindow其实就对应了NavigationBarFrame这个这个对象。
二、构建类型为NavigationBarFrame的顶部栏视图容器、底部栏视图容器、左侧栏视图容器、右侧栏视图容器所需要填充的视图内容。
1、CarSystemBar的createSystemBar方法在调用buildNavBarWindows方法构建NavigationBarFrame视图容器之后,会继续调用buildNavBarContent方法来为视图容器构建具体的视图内容。
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
...代码省略...
private void createSystemBar(RegisterStatusBarResult result) {
buildNavBarWindows();//构建视图对象容器
buildNavBarContent();//构建视图对象内容
attachNavBarWindows();
...代码省略...
}
...代码省略...
}
2、buildNavBarContent方法如下所示:
public class CarSystemBar extends SystemUI implements
...代码省略...
private boolean mDeviceIsSetUpForUser = true;
private boolean mIsUserSetupInProgress = false;
...代码省略...
private void buildNavBarContent() {
mTopSystemBarView = mCarSystemBarController.getTopBar(isDeviceSetupForUser());
if (mTopSystemBarView != null) {
mSystemBarConfigs.insetSystemBar(SystemBarConfigs.TOP, mTopSystemBarView);
mHvacController.registerHvacViews(mTopSystemBarView);
mTopSystemBarWindow.addView(mTopSystemBarView);
}
mBottomSystemBarView = mCarSystemBarController.getBottomBar(isDeviceSetupForUser());
if (mBottomSystemBarView != null) {
mSystemBarConfigs.insetSystemBar(SystemBarConfigs.BOTTOM, mBottomSystemBarView);
mHvacController.registerHvacViews(mBottomSystemBarView);
mBottomSystemBarWindow.addView(mBottomSystemBarView);
}
mLeftSystemBarView = mCarSystemBarController.getLeftBar(isDeviceSetupForUser());
if (mLeftSystemBarView != null) {
mSystemBarConfigs.insetSystemBar(SystemBarConfigs.LEFT, mLeftSystemBarView);
mHvacController.registerHvacViews(mLeftSystemBarView);
mLeftSystemBarWindow.addView(mLeftSystemBarView);
}
mRightSystemBarView = mCarSystemBarController.getRightBar(isDeviceSetupForUser());
if (mRightSystemBarView != null) {
mSystemBarConfigs.insetSystemBar(SystemBarConfigs.RIGHT, mRightSystemBarView);
mHvacController.registerHvacViews(mRightSystemBarView);
mRightSystemBarWindow.addView(mRightSystemBarView);
}
}
private boolean isDeviceSetupForUser() {
return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;
}
...代码省略...
}
和之前buildNavBarWindows调用CarSystemBarController的get_Window方法一样,buildNavBarContent方法会继续调用CarSystemBarController的另外一个方法get_Bar,来获取具体的视图内容。
3、CarSystemBarController和get_Bar方法关联的关键代码如下所示:
@SysUISingleton
public class CarSystemBarController {
...代码省略...
@Nullable
public CarSystemBarView getTopBar(boolean isSetUp) {
if (!mShowTop) {
return null;
}
mTopView = mCarSystemBarViewFactory.getTopBar(isSetUp);
//让mTopView视图内容对象和状态栏触摸监听对象、通知栏控制器、空调面板控制器产生关联
setupBar(mTopView, mTopBarTouchListener, mNotificationsShadeController,
mHvacPanelController, mHvacPanelOverlayViewController);
if (isSetUp) {
//对麦克风进行设置,系统不希望在unProvisioned模式下麦克风和配置文件选择器被点击
setupMicQcPanel();
//配置用户信息面板
setupProfilePanel();
}
return mTopView;
}
@Nullable
public CarSystemBarView getBottomBar(boolean isSetUp) {
if (!mShowBottom) {
return null;
}
mBottomView = mCarSystemBarViewFactory.getBottomBar(isSetUp);//获取底部栏具体视图内容
setupBar(mBottomView, mBottomBarTouchListener, mNotificationsShadeController,
mHvacPanelController, mHvacPanelOverlayViewController);
return mBottomView;
}
@Nullable
public CarSystemBarView getLeftBar(boolean isSetUp) {
if (!mShowLeft) {
return null;
}
mLeftView = mCarSystemBarViewFactory.getLeftBar(isSetUp);//获取左侧栏具体视图内容
setupBar(mLeftView, mLeftBarTouchListener, mNotificationsShadeController,
mHvacPanelController, mHvacPanelOverlayViewController);
return mLeftView;
}
@Nullable
public CarSystemBarView getRightBar(boolean isSetUp) {
if (!mShowRight) {
return null;
}
mRightView = mCarSystemBarViewFactory.getRightBar(isSetUp);//获取右侧栏具体视图内容
setupBar(mRightView, mRightBarTouchListener, mNotificationsShadeController,
mHvacPanelController, mHvacPanelOverlayViewController);
return mRightView;
}
//让CarSystemBarView视图内容对象和状态栏触摸监听对象、通知栏控制器、空调面板控制器产生关联
private void setupBar(CarSystemBarView view, View.OnTouchListener statusBarTouchListener,
NotificationsShadeController notifShadeController,
HvacPanelController hvacPanelController,
HvacPanelOverlayViewController hvacPanelOverlayViewController) {
view.setStatusBarWindowTouchListener(statusBarTouchListener);
view.setNotificationsPanelController(notifShadeController);
view.setHvacPanelController(hvacPanelController);
view.registerHvacPanelOverlayViewController(hvacPanelOverlayViewController);
mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
mButtonRoleHolderController.addAllButtonsWithRoleName(view);
mUserNameViewControllerLazy.get().addUserNameView(view);
mPrivacyChipViewControllerLazy.get().addPrivacyChipView(view);
}
private void setupMicQcPanel() {
//状态栏图标控制器
if (mMicPanelController == null) {
mMicPanelController = new StatusIconPanelController(mContext, mCarServiceProvider,
mBroadcastDispatcher, mConfigurationController);
}
mMicPanelController.setOnQcViewsFoundListener(qcViews -> qcViews.forEach(qcView -> {
if (qcView.getLocalQCProvider() instanceof MicQcPanel) {
MicQcPanel micQcPanel = (MicQcPanel) qcView.getLocalQCProvider();
micQcPanel.setControllers(mPrivacyChipViewControllerLazy.get(),
mMicPrivacyElementsProviderLazy.get());
}
}));
mMicPanelController.attachPanel(mTopView.requireViewById(R.id.privacy_chip),
R.layout.qc_mic_panel, R.dimen.car_mic_qc_panel_width, mPrivacyChipXOffset,
mMicPanelController.getDefaultYOffset(), Gravity.TOP | Gravity.END);
}
//配置用户信息面板
private void setupProfilePanel() {
View profilePickerView = mTopView.findViewById(R.id.user_name);
if (mProfilePanelController == null && profilePickerView != null) {
boolean profilePanelDisabledWhileDriving = mContext.getResources().getBoolean(
R.bool.config_profile_panel_disabled_while_driving);
mProfilePanelController = new StatusIconPanelController(mContext, mCarServiceProvider,
mBroadcastDispatcher, mConfigurationController,
profilePanelDisabledWhileDriving);
mProfilePanelController.attachPanel(profilePickerView, R.layout.qc_profile_switcher,
R.dimen.car_profile_quick_controls_panel_width, Gravity.TOP | Gravity.END);
}
}
...代码省略...
}
四个视图对象获取视图内容的方式是非常相似的,get_Bar方法首先判断是否显示对应的栏,如果确定显示则会继续调用CarSystemBarViewFactory的get_Bar方法来获取具体的视图内容,随后调用setupBar方法让视图内容对象和状态栏触摸监听对象、通知栏控制器、空调面板控制器产生关联。其中getTopBar方法还会继续调用setupMicQcPanel方法和setupProfilePanel方法来进行一些额外的设置。
4、CarSystemBarViewFactory类关于get_Bar方法的关键代码方法如下所示:
@SysUISingleton
public class CarSystemBarViewFactory {
...代码省略...
private final ArrayMap<Type, CarSystemBarView> mCachedViewMap = new ArrayMap<>(Type.values().length);
private static final ArrayMap<Type, Integer> sLayoutMap = setupLayoutMapping();
private static ArrayMap<Type, Integer> setupLayoutMapping() {
ArrayMap<Type, Integer> map = new ArrayMap<>();
map.put(Type.TOP, R.layout.car_top_system_bar);//顶部栏视图布局
map.put(Type.TOP_UNPROVISIONED, R.layout.car_top_system_bar_unprovisioned);
map.put(Type.BOTTOM, R.layout.car_bottom_system_bar);//底部栏视图布局
map.put(Type.BOTTOM_UNPROVISIONED, R.layout.car_bottom_system_bar_unprovisioned);
map.put(Type.LEFT, R.layout.car_left_system_bar);//左侧栏视图布局
map.put(Type.LEFT_UNPROVISIONED, R.layout.car_left_system_bar_unprovisioned);
map.put(Type.RIGHT, R.layout.car_right_system_bar);//右侧栏视图布局
map.put(Type.RIGHT_UNPROVISIONED, R.layout.car_right_system_bar_unprovisioned);
return map;
}
...代码省略...
//获取顶部栏视图内容
public CarSystemBarView getTopBar(boolean isSetUp) {
return getBar(isSetUp, Type.TOP, Type.TOP_UNPROVISIONED);
}
//获取底部栏视图内容
public CarSystemBarView getBottomBar(boolean isSetUp) {
return getBar(isSetUp, Type.BOTTOM, Type.BOTTOM_UNPROVISIONED);
}
//获取左侧栏视图内容
public CarSystemBarView getLeftBar(boolean isSetUp) {
return getBar(isSetUp, Type.LEFT, Type.LEFT_UNPROVISIONED);
}
//获取右侧栏视图内容
public CarSystemBarView getRightBar(boolean isSetUp) {
return getBar(isSetUp, Type.RIGHT, Type.RIGHT_UNPROVISIONED);
}
//getBar继续调用getBarCached方法
private CarSystemBarView getBar(boolean isSetUp, Type provisioned, Type unprovisioned) {
CarSystemBarView view = getBarCached(isSetUp, provisioned, unprovisioned);
if (view == null) {
String name = isSetUp ? provisioned.name() : unprovisioned.name();
Log.e(TAG, "CarStatusBar failed inflate for " + name);
throw new RuntimeException(
"Unable to build " + name + " nav bar due to missing layout");
}
return view;
}
//getBarCached方法最终是从sLayoutMap中获取具体的视图内容
private CarSystemBarView getBarCached(boolean isSetUp, Type provisioned, Type unprovisioned) {
Type type = isSetUp ? provisioned : unprovisioned;
if (mCachedViewMap.containsKey(type)) {
return mCachedViewMap.get(type);
}
//从sLayoutMap中获取对应的布局文件资源id
@LayoutRes int barLayout = sLayoutMap.get(type);
//将布局文件转化为CarSystemBarView类型的View对象。
CarSystemBarView view = (CarSystemBarView) View.inflate(mContext, barLayout,
/* root= */ null);
//为空调按钮设置点击事件
view.setupHvacButton();
//为快捷图标容器设置控制器
view.setupQuickControlsEntryPoints(mQuickControlsEntryPointsController, isSetUp);
//为只读图标设置控制器
view.setupReadOnlyIcons(mReadOnlyIconsController);
view.addView(new FocusParkingView(mContext), 0);
//将视图内容View添加到缓存中
mCachedViewMap.put(type, view);
//返回当前type对应的视图内容View
return mCachedViewMap.get(type);
}
}
CarSystemBarViewFactory这个类的get_Bar方法会继续调用getBarCached方法,getBarCached首先从类型为ArrayMap<Type, CarSystemBarView>的缓存mCachedViewMap中获取缓存对象,如果存在直接返回,如果不存在则继续从类型为ArrayMap<Type, Integer>的sLayoutMap中获取具体布局文件资源id,结合
private static final ArrayMap<Type, Integer> sLayoutMap = setupLayoutMapping();
private static ArrayMap<Type, Integer> setupLayoutMapping() {
ArrayMap<Type, Integer> map = new ArrayMap<>();
map.put(Type.TOP, R.layout.car_top_system_bar);//顶部栏视图布局
map.put(Type.BOTTOM, R.layout.car_bottom_system_bar);//底部栏视图布局
map.put(Type.LEFT, R.layout.car_left_system_bar);//左侧栏视图布局
map.put(Type.RIGHT, R.layout.car_right_system_bar);//右侧栏视图布局
return map;
}
我们可以知道顶部栏对应R.layout.car_top_system_bar,底部栏对应R.layout.car_bottom_system_bar,左侧栏对应R.layout.car_left_system_bar,右侧栏对应R.layout.car_right_system_bar,这里在从sLayoutMap中获取到对应的布局文件资源id后,再通过View的inflate方法将布局文件转化为相对应的视图对象,并且会调用setupHvacButton为空调按钮设置点击事件,调用setupQuickControlsEntryPoints为快捷图标容器设置控制器,调用setupReadOnlyIcons为只读图标设置控制器,最后会将CarSystemBarView存储到mCachedViewMap缓存中,并返回CarSystemBarView视图对象。
三、将顶部栏、底部栏、左侧栏、右侧栏添加到Window中。
1、重新回到CarSystemBar的createSystemBar方法
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
private void createSystemBar(RegisterStatusBarResult result) {
buildNavBarWindows();//构建视图对象容器
buildNavBarContent();//构建视图对象内容
attachNavBarWindows();//将视图对象添加到Window中
...代码省略...
}
}
前面我们已经分析了构建视图对象容器和构建视图对象内容,接下来我们继续分析attachNavBarWindows方法,是该方法将将视图对象添加到Window中。
2、attachNavBarWindows方法代码如下:
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
private void attachNavBarWindows() {
mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(this::attachNavBarBySide);
}
}
attachNavBarWindows会调用SystemBarConfigs的getSystemBarSidesByZOrder方法获取到当前存在的所有SystemBar所对应的Side。
3、SystemBarConfigs类和getSystemBarSidesByZOrder方法相关的关键代码如下所示:
@SysUISingleton
public class SystemBarConfigs {
private final Map<@SystemBarSide Integer, SystemBarConfig> mSystemBarConfigMap =
new ArrayMap<>();
private final List<@SystemBarSide Integer> mSystemBarSidesByZOrder = new ArrayList<>();
@Inject
public SystemBarConfigs(@Main Resources resources) {
...代码省略...
readConfigs();//读取SystemBar所对应的SystemBarConfig的配置信息
...代码省略...
sortSystemBarSidesByZOrder();//使用SystemBarConfig的ZOrder属性对SystemBarConfig的Size进行排序
}
private void readConfigs() {
// <bool name="config_enableTopSystemBar">true</bool>
mTopNavBarEnabled = mResources.getBoolean(R.bool.config_enableTopSystemBar);
// <bool name="config_enableBottomSystemBar">true</bool>
mBottomNavBarEnabled = mResources.getBoolean(R.bool.config_enableBottomSystemBar);
// <bool name="config_enableLeftSystemBar">false</bool>
mLeftNavBarEnabled = mResources.getBoolean(R.bool.config_enableLeftSystemBar);
// <bool name="config_enableRightSystemBar">false</bool>
mRightNavBarEnabled = mResources.getBoolean(R.bool.config_enableRightSystemBar);
//顶部栏可用
if (mTopNavBarEnabled) {
SystemBarConfig topBarConfig =
new SystemBarConfigBuilder()
.setSide(TOP)
.setGirth(mResources.getDimensionPixelSize(
R.dimen.car_top_system_bar_height))//顶部栏高度
.setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
//<integer name="config_topSystemBarZOrder">1</integer>
.setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
.setHideForKeyboard(mResources.getBoolean(
R.bool.config_hideTopSystemBarForKeyboard))
.build();
mSystemBarConfigMap.put(TOP, topBarConfig);
}
//底部栏
if (mBottomNavBarEnabled) {
SystemBarConfig bottomBarConfig =
new SystemBarConfigBuilder()
.setSide(BOTTOM)
.setGirth(mResources.getDimensionPixelSize(
R.dimen.car_bottom_system_bar_height))//底部栏高度
.setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
//<integer name="config_bottomSystemBarZOrder">10</integer>
.setZOrder(mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
.setHideForKeyboard(mResources.getBoolean(
R.bool.config_hideBottomSystemBarForKeyboard))
.build();
mSystemBarConfigMap.put(BOTTOM, bottomBarConfig);
}
//左侧栏不可用
if (mLeftNavBarEnabled) {
SystemBarConfig leftBarConfig =
new SystemBarConfigBuilder()
.setSide(LEFT)
.setGirth(mResources.getDimensionPixelSize(
R.dimen.car_left_system_bar_width))
.setBarType(mResources.getInteger(R.integer.config_leftSystemBarType))
.setZOrder(mResources.getInteger(R.integer.config_leftSystemBarZOrder))
.setHideForKeyboard(mResources.getBoolean(
R.bool.config_hideLeftSystemBarForKeyboard))
.build();
mSystemBarConfigMap.put(LEFT, leftBarConfig);
}
//右侧栏不可用
if (mRightNavBarEnabled) {
SystemBarConfig rightBarConfig =
new SystemBarConfigBuilder()
.setSide(RIGHT)
.setGirth(mResources.getDimensionPixelSize(
R.dimen.car_right_system_bar_width))
.setBarType(mResources.getInteger(R.integer.config_rightSystemBarType))
.setZOrder(mResources.getInteger(R.integer.config_rightSystemBarZOrder))
.setHideForKeyboard(mResources.getBoolean(
R.bool.config_hideRightSystemBarForKeyboard))
.build();
mSystemBarConfigMap.put(RIGHT, rightBarConfig);
}
}
//根据序号对SystemBar进行排序
private void sortSystemBarSidesByZOrder() {
//获取SystemBarConfig列表
List<SystemBarConfig> systemBarsByZOrder = new ArrayList<>(mSystemBarConfigMap.values());
systemBarsByZOrder.sort(new Comparator<SystemBarConfig>() {
@Override
public int compare(SystemBarConfig o1, SystemBarConfig o2) {
//调用SystemBarConfig的getZOrder进行大小比较
return o1.getZOrder() - o2.getZOrder();
}
});
//在mSystemBarSidesByZOrder中存储当前按照ZOrder从小到大来进行排序的SystemBar的Side数值。
systemBarsByZOrder.forEach(systemBarConfig -> {
mSystemBarSidesByZOrder.add(systemBarConfig.getSide());
});
}
protected List<Integer> getSystemBarSidesByZOrder() {
return mSystemBarSidesByZOrder;
}
SystemBarConfig对象本身
private static final class SystemBarConfig {
private final int mSide;
private final int mBarType;
private final int mGirth;
private final int mZOrder;
private final boolean mHideForKeyboard;
private int[] mPaddings = new int[]{0, 0, 0, 0};
private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder,
boolean hideForKeyboard) {
mSide = side;
mBarType = barType;
mGirth = girth;
mZOrder = zOrder;
mHideForKeyboard = hideForKeyboard;
}
...代码省略...
private WindowManager.LayoutParams getLayoutParams() {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
isHorizontalBar(mSide) ? ViewGroup.LayoutParams.MATCH_PARENT : mGirth,
isHorizontalBar(mSide) ? mGirth : ViewGroup.LayoutParams.MATCH_PARENT,
mapZOrderToBarType(mZOrder),
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);//设置窗口半透明
// PixelFormat.TRANSPARENT);//设置窗口全透明
lp.setTitle(BAR_TITLE_MAP.get(mSide));
lp.providesInsetsTypes = new int[]{BAR_TYPE_MAP[mBarType], BAR_GESTURE_MAP.get(mSide)};
lp.setFitInsetsTypes(0);
lp.windowAnimations = 0;
lp.gravity = BAR_GRAVITY_MAP.get(mSide);
return lp;
}
private int mapZOrderToBarType(int zOrder) {
//<integer name="config_topSystemBarZOrder">1</integer>
//<integer name="config_bottomSystemBarZOrder">10</integer>
//从这里可以知道顶部栏的窗口类型为TYPE_NAVIGATION_BAR_PANEL
//从这里可以知道底部栏的窗口类型为TYPE_STATUS_BAR_ADDITIONAL
return zOrder >= HUN_ZORDER ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL
: WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
}
private void setPaddingBySide(@SystemBarSide int side, int padding) {
mPaddings[side] = padding;
}
}
SystemBarConfig对象的建造者
private static final class SystemBarConfigBuilder {
private int mSide;
private int mBarType;
private int mGirth;
private int mZOrder;
private boolean mHideForKeyboard;
private SystemBarConfigBuilder setSide(@SystemBarSide int side) {
mSide = side;
return this;
}
private SystemBarConfigBuilder setBarType(int type) {
mBarType = type;
return this;
}
private SystemBarConfigBuilder setGirth(int girth) {
mGirth = girth;
return this;
}
private SystemBarConfigBuilder setZOrder(int zOrder) {
mZOrder = zOrder;
return this;
}
private SystemBarConfigBuilder setHideForKeyboard(boolean hide) {
mHideForKeyboard = hide;
return this;
}
private SystemBarConfig build() {
return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard);
}
}
总结一下以上代码:
-
在SystemBarConfigs的构造方法中,调用readConfigs方法来读取SystemBar所对应的SystemBarConfig的配置信息,结合注释可以发现默认情况下,顶部栏和底部栏可用半透明,且顶部栏的窗口类型为TYPE_NAVIGATION_BAR_PANEL,底部栏的窗口类型为TYPE_STATUS_BAR_ADDITIONAL。且它们所对应的配置信息会存储在mSystemBarConfigMap中,左侧栏和右侧栏不可用且它们所对应的配置信息不会存储在mSystemBarConfigMap中。
-
SystemBarConfigs的构造方法继续调用sortSystemBarSidesByZOrder,该方法会根据已经存储在mSystemBarConfigMap中的SystemBarConfigs的ZOrder字段来进行排序,将mSystemBarConfigMap中SystemBarConfigs的Side字段存储在类型为int的mSystemBarSidesByZOrder集合中。
4、明白了SystemBarConfigs的主要功能,再重新看第2步的attachNavBarWindows方法。
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
private void attachNavBarWindows() {
mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(this::attachNavBarBySide);
}
}
我们可以知道该方法最终会循环mSystemBarSidesByZOrder集合的内容,用该集合的子项作为参数,依次调用attachNavBarBySide方法。
5、attachNavBarBySide方法如下所示:
public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {
private void attachNavBarBySide(int side) {
switch (side) {
case SystemBarConfigs.TOP:
if (mTopSystemBarWindow != null) {
//如果顶部栏视图容器不为空,将顶部栏视图容器添加到Window中
mWindowManager.addView(mTopSystemBarWindow,
mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.TOP));
}
break;
case SystemBarConfigs.BOTTOM:
//如果底部栏视图容器不为空,将顶部栏视图容器添加到Window中
if (mBottomSystemBarWindow != null && !mBottomNavBarVisible) {
mBottomNavBarVisible = true;
mWindowManager.addView(mBottomSystemBarWindow,
mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.BOTTOM));
}
break;
case SystemBarConfigs.LEFT:
if (mLeftSystemBarWindow != null) {
mWindowManager.addView(mLeftSystemBarWindow,
mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.LEFT));
}
break;
case SystemBarConfigs.RIGHT:
if (mRightSystemBarWindow != null) {
mWindowManager.addView(mRightSystemBarWindow,
mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.RIGHT));
}
break;
default:
return;
}
}
}
attachNavBarBySide做的方法并不多,就是根据对应的type判断当前视图容器的具体类型,到底是顶部栏、底部栏、左侧栏还是右侧栏,根据类型配合类型相对应的参数将该视图容器添加到WindowManager中。
到这里我们已经把CarSystemBar从启动到构建视图,再到将视图添加到Window的流程整理分析完毕了,结合代码我们可以知道,默认情况下在是车载系统中只显示顶部栏和底部栏视图的。