美文网首页
Android12 CarSystemBar源码分析<一> 从创

Android12 CarSystemBar源码分析<一> 从创

作者: AFinalStone | 来源:发表于2022-11-10 19:25 被阅读0次

前言

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的流程整理分析完毕了,结合代码我们可以知道,默认情况下在是车载系统中只显示顶部栏和底部栏视图的。

相关文章

网友评论

      本文标题:Android12 CarSystemBar源码分析<一> 从创

      本文链接:https://www.haomeiwen.com/subject/uchatdtx.html