前言

上一篇文章我们具体分析了Android12原生状态栏类StatusBar构建状态栏视图以及将状态栏添加到Window的过程,也分析了状态栏视图的整个布局结构;早期的状态栏和导航栏对于手机设备来说那是相当重要的,但是随着手机不断的推陈出新,状态栏和导航栏对于手机的重要性在逐渐降低,特别是在快捷手势出现之后,导航栏几乎变得可有可无。但是对于当前如火如荼的商用车载系统来说,状态栏和导航栏却几乎是必备的,谷歌自然也意识到了这点,特意在Android12的原生代码中提供了一个包含有状态栏、导航栏、消息中心等SystemUI组件的项目CarSystemUI,而它正是本篇文章我们所要分析的。

一、车载SystemUI和原生SystemUI的关系

1)两者的位置不同

原生SystemUI项目位于/frameworks/base/package/SystemUI目录,车载SystemUI项目位于/packages/apps/Car/SystemUI目录

2)原生SystemUI是车载SystemUI的基础

原生SystemUI可以独立编译成SystemUI.apk文件,车载SystemUI无法独立编译,必须在原生SystemUI的基础上才能编译出CarSystemUI.apk文件;通过阅读CarSystemUI的Android.bp文件可以发现,CarSystemUI在编译的时候,会将原生的SystemUI以静态库的方式引入。

packages/apps/Car/SystemUI/Android.bp

android_library {
    name: "CarSystemUI-core",

    srcs: [
        "src/**/*.java",
        "src/**/I*.aidl",
    ],

    resource_dirs: [
        "res-keyguard",
        "res",
    ],

    static_libs: [
        "SystemUI-core",
        "CarNotificationLib",
        "SystemUIPluginLib",
        "SystemUISharedLib",
        "SettingsLib",
        "car-admin-ui-lib",
        "car-ui-lib",
        "android.car.userlib",
        "car-qc-lib",
        "androidx.legacy_legacy-support-v4",
        "androidx.recyclerview_recyclerview",
        "androidx.preference_preference",
        "androidx.appcompat_appcompat",
        "androidx.mediarouter_mediarouter",
        "androidx.palette_palette",
        "androidx.legacy_legacy-preference-v14",
        "androidx.leanback_leanback",
        "androidx.slice_slice-core",
        "androidx.slice_slice-view",
        "androidx.slice_slice-builders",
        "androidx.arch.core_core-runtime",
        "androidx.lifecycle_lifecycle-extensions",
        "SystemUI-tags",
        "SystemUI-proto",
        "dagger2",
        "//external/kotlinc:kotlin-annotations",
    ],

    libs: [
        "android.car",
    ],

    manifest: "AndroidManifest.xml",

    plugins: ["dagger2-compiler"],

}

android_app {
    name: "CarSystemUI",

    static_libs: [
        "CarSystemUI-core",
    ],

    export_package_resources: true,

    libs: [
        "android.car",
    ],

    resource_dirs: [],

     overrides: [
        "SystemUI",
    ],

    platform_apis: true,
    system_ext_specific: true,
    certificate: "platform",
    privileged: true,

    optimize: {
        proguard_flags_files: [
            "proguard.flags",
        ],
    },
    dxflags: ["--multi-dex"],

    aaptflags: [
        "--extra-packages",
        "com.android.keyguard",
    ],

    kotlincflags: ["-Xjvm-default=enable"],

    plugins: ["dagger2-compiler"],

    required: ["privapp_whitelist_com.android.systemui", "allowed_privapp_com.android.carsystemui"],
}

3)两者包含的组件不同

车载SystemUI去掉了原生SystemUI中的一部分组件,又新增了一部分自定义组件。

二、CarSystemUIFactory创建CarSystemUI模块所需的各种SystemUI组件

1、在Android 12系统源码_SystemUI(一)SystemUI的启动流程这篇文章中我们有讲过,SystemUIApplication的onCreate方法会回调对象SystemUIAppComponentFactory为自己设置的回调方法。

frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java

public class SystemUIAppComponentFactory extends AppComponentFactory {

    @NonNull
    @Override
    public Application instantiateApplicationCompat(
            @NonNull ClassLoader cl, @NonNull String className)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Application app = super.instantiateApplicationCompat(cl, className);
        if (app instanceof ContextInitializer) {
        	//为Application设置回调方法
            ((ContextInitializer) app).setContextAvailableCallback(
                    context -> {
                        SystemUIFactory.createFromConfig(context);
                        SystemUIFactory.getInstance().getSysUIComponent().inject(SystemUIAppComponentFactory.this);
                    }
            );
        }

        return app;
    }

}

2、该回调方法会调用SystemUIFactory的createFromConfig方法。

framework/base/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java

public class SystemUIFactory {

    static SystemUIFactory mFactory;
    
    public static <T extends SystemUIFactory> T getInstance() {
        return (T) mFactory;
    }
    public static void createFromConfig(Context context) {
        createFromConfig(context, false);
    }

    @VisibleForTesting
    public static void createFromConfig(Context context, boolean fromTest) {
        if (mFactory != null) {
            return;
        }
        //获取config_systemUIFactoryComponent所对应的字符串
        final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
        if (clsName == null || clsName.length() == 0) {
            throw new RuntimeException("No SystemUIFactory component configured");
        }
        try {
            Class<?> cls = null;
            cls = context.getClassLoader().loadClass(clsName);
            //通过反射创建SystemUIFactory实例并赋值给静态属性mFactory 
            mFactory = (SystemUIFactory) cls.newInstance();
            //执行init方法
            mFactory.init(context, fromTest);
        } catch (Throwable t) {
            Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
            throw new RuntimeException(t);
        }
    }
}

frameworks/base/packages/SystemUI/res/values/config.xml

<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>

SystemUIFactory的createFromConfig最终是获取R.string.config_systemUIFactoryComponent所对应的字符串,在原生SystemUI项目中这个字符串就是SystemUIFactory自己的类名,这样通过反射创建的对象自然就是SystemUIFactory本身,后续调用SystemUIFactory的getInstance方法所获取的对象自然也是SystemUIFactory对象实例。

3、然而车载SystemUI项目修改了这个字段,让该字段变成了CarSystemUIFactory的类名。

packages/apps/Car/SystemUI/res/values/config.xml

<string name="config_systemUIFactoryComponent" translatable="false">
        com.android.systemui.CarSystemUIFactory
    </string>

这样SystemUIFactory后续调用SystemUIFactory的getInstance方法所获取的对象就变成了CarSystemUIFactory。

4、SystemUI组件最初都是在SystemUIApplication的startServicesIfNeeded方法中被创建并初始化的。

public void startServicesIfNeeded() {
        String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
        startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
    }

SystemUIApplication的startServicesIfNeeded方法调用SystemUIFactory.getInstance()方法最终得到了CarSystemUIFactory对象,接着会调用CarSystemUIFactory的getSystemUIServiceComponents方法获取需要创建和初始化的SystemUI组件对象。

5、CarSystemUIFactory的getSystemUIServiceComponents方法如下所示。

public class CarSystemUIFactory extends SystemUIFactory {
    @Override
    public String[] getSystemUIServiceComponents(Resources resources) {
        Set<String> names = new HashSet<>();
		//获取SystemUI项目配置的所有SystemUI组件
        for (String s : super.getSystemUIServiceComponents(resources)) {
            names.add(s);
        }
		//CarSystemUI模块需要移除的SystemUI组件
        for (String s : resources.getStringArray(R.array.config_systemUIServiceComponentsExclude)) {
            names.remove(s);
        }
		//CarSystemUI模块需要新增的SystemUI组件
        for (String s : resources.getStringArray(R.array.config_systemUIServiceComponentsInclude)) {
            names.add(s);
        }
        //在经过移除、新增两步后,会将新的组件数组返回
        String[] finalNames = new String[names.size()];
        names.toArray(finalNames);
        return finalNames;
    }
 }

1)获取SystemUI项目配置的所有SystemUI组件。

<string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.util.NotificationChannels</item><!--通知-->
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item><!--键盘锁状态-->
        <item>com.android.systemui.recents.Recents</item><!--任务列表-->
        <item>com.android.systemui.volume.VolumeUI</item><!--监听音量,并决定是否显示音量的对话框-->
        <item>com.android.systemui.statusbar.phone.StatusBar</item><!--状态栏-->
        <item>com.android.systemui.usb.StorageNotification</item><!--监听 USB 连接状态并发送通知进行提示-->
        <item>com.android.systemui.power.PowerUI</item><!--监听电量状态并在低电量时发送通知-->
        <item>com.android.systemui.media.RingtonePlayer</item><!--用于播放铃声-->
        <item>com.android.systemui.keyboard.KeyboardUI</item><!--键盘锁 UI-->
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item><!--快捷分发器-->
        <item>@string/config_systemUIVendorServiceComponent</item><!--这里可以定义厂商定制的组件-->
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item><!--用于监控内存泄漏的服务-->
        <item>com.android.systemui.LatencyTester</item><!--仅在 debug 环境执行,用于监听系统测试延迟的模拟动作-->
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item><!--用于显示全局对话框(例如长按电源按键)-->
        <item>com.android.systemui.ScreenDecorations</item><!--处理页面中的显示的形状(如圆角)-->
        <item>com.android.systemui.biometrics.AuthController</item><!--身份验证-->
        <item>com.android.systemui.SliceBroadcastRelayHandler</item><!--允许打开设置App-->
        <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item><!--时应用程序通知-->
        <item>com.android.systemui.theme.ThemeOverlayController</item><!--主题-->
        <item>com.android.systemui.accessibility.WindowMagnification</item><!--放大器-->
        <item>com.android.systemui.accessibility.SystemActions</item>
        <item>com.android.systemui.toast.ToastUI</item>
        <item>com.android.systemui.wmshell.WMShell</item>
    </string-array>

2)CarSystemUI模块需要移除的SystemUI组件。

<string-array name="config_systemUIServiceComponentsExclude" translatable="false">
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.statusbar.phone.StatusBar</item><!--原生状态栏在这里被移除-->
        <item>com.android.systemui.keyboard.KeyboardUI</item>
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
        <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
        <item>com.android.systemui.accessibility.WindowMagnification</item>
        <item>com.android.systemui.accessibility.SystemActions</item>
        <item>com.android.systemui.toast.ToastUI</item>
    </string-array>

3)CarSystemUI模块需要新增的SystemUI组件。

<string-array name="config_systemUIServiceComponentsInclude" translatable="false">
        <item>com.android.systemui.car.systembar.CarSystemBar</item><!--车载导航栏在这里被添加-->
        <item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier</item>
        <item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item>
        <item>com.android.systemui.car.toast.CarToastUI</item>
        <item>com.android.systemui.car.volume.VolumeUI</item>
        <item>com.android.systemui.car.cluster.ClusterDisplayController</item>
    </string-array>

到这里我们终于看到了车载导航栏CarSystemBar组件,前面的文章我们有介绍,SystemUIApplication的startServicesIfNeeded会创建所有的SystemUI组件对象并调用它们的start方法。

三、从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构造方法中的所有参数赋值,紧接着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();
        ...代码省略...
    }
    ...代码省略...
}

createSystemBar首先调用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方法相关的关键代码如下所示:

packages/apps/Car/SystemUI/src/com/android/systemui/car/systembar/SystemBarConfigs.java

@SysUISingleton
public class SystemBarConfigs {
    private static final int HUN_ZORDER = 10;
    public static final int TOP = 0;
    public static final int BOTTOM = 1;
    public static final int LEFT = 2;
    public static final int RIGHT = 3;
    
    private static final int[] BAR_TYPE_MAP = {
            InsetsState.ITYPE_STATUS_BAR,//状态栏对应的系统装饰窗口类型
            InsetsState.ITYPE_NAVIGATION_BAR,//导航栏对应的系统装饰窗口类型
            InsetsState.ITYPE_CLIMATE_BAR,
            InsetsState.ITYPE_EXTRA_NAVIGATION_BAR
    };
    private final Map<@SystemBarSide Integer, SystemBarConfig> mSystemBarConfigMap =
            new ArrayMap<>();
    private final List<@SystemBarSide Integer> mSystemBarSidesByZOrder = new ArrayList<>();
    @Inject
    public SystemBarConfigs(@Main Resources resources) {
        ...代码省略...
        populateMaps();
        readConfigs();//读取SystemBar所对应的SystemBarConfig的配置信息
        ...代码省略...
        sortSystemBarSidesByZOrder();//使用SystemBarConfig的ZOrder属性对SystemBarConfig的Size进行排序
    }
    
    private static void populateMaps() {
        BAR_GRAVITY_MAP.put(TOP, Gravity.TOP);
        BAR_GRAVITY_MAP.put(BOTTOM, Gravity.BOTTOM);
        BAR_GRAVITY_MAP.put(LEFT, Gravity.LEFT);
        BAR_GRAVITY_MAP.put(RIGHT, Gravity.RIGHT);

        BAR_TITLE_MAP.put(TOP, "TopCarSystemBar");
        BAR_TITLE_MAP.put(BOTTOM, "BottomCarSystemBar");
        BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar");
        BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar");

        BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_MANDATORY_GESTURES);
        BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES);
        BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_MANDATORY_GESTURES);
        BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_MANDATORY_GESTURES);
    }
    
  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)
                            //顶部栏高度<dimen name="car_top_system_bar_height">@*android:dimen/status_bar_height</dimen>
                            .setGirth(mResources.getDimensionPixelSize(R.dimen.car_top_system_bar_height))
                            //系统栏类型<integer name="config_topSystemBarType">0</integer>
                            .setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
                            //系统栏Z轴序列<integer name="config_topSystemBarZOrder">1</integer>
                            .setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
                            //<bool name="config_hideTopSystemBarForKeyboard">false</bool>
                            .setHideForKeyboard(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard))
                            .build();
            mSystemBarConfigMap.put(TOP, topBarConfig);
        }
        //底部栏可用
        if (mBottomNavBarEnabled) {
            SystemBarConfig bottomBarConfig =
                    new SystemBarConfigBuilder()
                            .setSide(BOTTOM)
                            //底部栏高度<dimen name="car_bottom_system_bar_height">@*android:dimen/navigation_bar_height</dimen>
                            .setGirth(mResources.getDimensionPixelSize(R.dimen.car_bottom_system_bar_height))
                            //系统栏类型<<integer name="config_bottomSystemBarType">1</integer>
                            .setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
                            //系统栏Z轴序列<integer name="config_bottomSystemBarZOrder">10</integer>
                            .setZOrder(mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
                   //<boolname="config_hideBottomSystemBarForKeyboard">@*android:bool/config_automotiveHideNavBarForKeyboard</bool>
                            .setHideForKeyboard(mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard))//默认为true
                            .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));
            //顶部栏为new int[]{InsetsState.ITYPE_STATUS_BAR, InsetsState.ITYPE_TOP_MANDATORY_GESTURES};
            //底部栏为new int[]{InsetsState.ITYPE_NAVIGATION_BAR, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES};
            //providesInsetsTypes这个字段很重要,只有设置对这个字段,系统才会认定该窗口是对应的装饰窗口;
            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>
            //zOrder = 10;
            //从这里可以知道顶部栏的窗口类型为TYPE_STATUS_BAR_ADDITIONAL
            //从这里可以知道底部栏的窗口类型为TYPE_NAVIGATION_BAR_PANEL
            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_STATUS_BAR_ADDITIONAL,底部栏的窗口类型为TYPE_NAVIGATION_BAR_PANEL。且它们所对应的配置信息会存储在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的流程;结合代码我们可以知道,默认情况下Android车载系统中只显示顶部栏和底部栏视图,最后用一张图对前面的代码流程做一个回顾。

仿ios状态栏教程 仿苹果状态栏ios12下载_仿ios状态栏教程