美文网首页
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