Android SystemUI——CarSystemBar添加到窗口(十)

devtools/2025/1/19 23:55:28/

        上一篇文章我们看到了车载状态栏 CarSystemBar 视图的创建流程,这里我们继续分析将车载状态栏添加到 Windows 窗口中。

一、添加状态栏到窗口

        前面我们已经分析了构建视图对象容器和构建视图对象内容,接下来我们继续分析 attachNavBarWindows() 方法将视图对象添加到 Window 中。

1、CarSystemBar

源码位置:/packages/apps/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBar.java

attachNavBarWindows

private final SystemBarConfigs mSystemBarConfigs;private void attachNavBarWindows() {mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(this::attachNavBarBySide);
}

        attachNavBarWindows() 会调用 SystemBarConfigs 的 getSystemBarSidesByZOrder() 方法获取到当前存在的所有 SystemBar 所对应的 Side。

2、SystemBarConfigs

源码位置:/packages/apps/Car/SystemUI/src/com/android/systemui/car/systembar/SystemBarConfigs.java

private final List<@SystemBarSide Integer> mSystemBarSidesByZOrder = new ArrayList<>();protected List<Integer> getSystemBarSidesByZOrder() {return mSystemBarSidesByZOrder;
}

        可以看到这里返回一个 List<@SystemBarSide Integer> 类型的数组,下面看一下 mSystemBarSidesByZOrder 数据的赋值。

sortSystemBarSidesByZOrder

private final Map<@SystemBarSide Integer, SystemBarConfig> mSystemBarConfigMap = new ArrayMap<>();private void sortSystemBarSidesByZOrder() {// 获取SystemBarConfig列表List<SystemBarConfig> systemBarsByZOrder = new ArrayList<>(mSystemBarConfigMap.values());systemBarsByZOrder.sort(new Comparator<SystemBarConfig>() {@Overridepublic int compare(SystemBarConfig o1, SystemBarConfig o2) {// 进行大小比较return o1.getZOrder() - o2.getZOrder();}});// 存储排序后SystemBar的Side数值。systemBarsByZOrder.forEach(systemBarConfig -> {mSystemBarSidesByZOrder.add(systemBarConfig.getSide());});
}

        这里首先对 SystemBarConfig 列表根据序号对 SystemBar 进行排序,并将排序后 SystemBar 的 Side 数值保存到  mSystemBarSidesByZOrder 数组中。而该函数是在 SystemBarConfigs 构造函数中进行调用,并且在调用之前还要填充 mSystemBarConfigMap 数据。

SystemBarConfigs

@Inject
public SystemBarConfigs(@Main Resources resources) {mResources = resources;// 初始化数据populateMaps();// 读取SystemBar所对应的SystemBarConfig的配置信息readConfigs();……// 使用SystemBarConfig的ZOrder属性对SystemBarConfig的Size进行排序sortSystemBarSidesByZOrder();
}

readConfigs

private void readConfigs() {mTopNavBarEnabled = mResources.getBoolean(R.bool.config_enableTopSystemBar);mBottomNavBarEnabled = mResources.getBoolean(R.bool.config_enableBottomSystemBar);mLeftNavBarEnabled = mResources.getBoolean(R.bool.config_enableLeftSystemBar);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))// 系统栏Z轴序列.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)).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);}
}

        这里首先获取状态了的可用状态,从 config.xml 文件中获取对应数据。

<!-- 配置应该显示哪些系统条 -->
<bool name="config_enableTopSystemBar">true</bool>
<bool name="config_enableLeftSystemBar">false</bool>
<bool name="config_enableRightSystemBar">false</bool>
<bool name="config_enableBottomSystemBar">true</bool>

        可以看到,这里配置了状态栏的显示状态,所以修改进度条的显示位置可以通过修改该数据实现。 同样其他相关的配置信息也都可以在对应的配置文件中找到。

3、添加状态栏

        我们知道 attachNavBarWindows() 方法最终会循环 mSystemBarSidesByZOrder 集合的内容,用该集合的子项作为参数,依次调用 attachNavBarBySide() 方法。

attachNavBarBySide

private void attachNavBarBySide(int side) {switch (side) {case SystemBarConfigs.TOP:// 如果顶部栏视图容器不为空,将顶部栏视图容器添加到Window中if (mTopSystemBarWindow != null) {mWindowManager.addView(mTopSystemBarWindow,mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.TOP));}break;case SystemBarConfigs.BOTTOM: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;}
}

        该方法就是根据对应的 type 判断当前视图容器的具体类型,到底是顶部栏、底部栏、左侧栏还是右侧栏,根据类型配合类型相对应的参数将该视图容器添加到 WindowManager 中。 

二、状态栏配置

        状态栏配置类 SystemBarConfig 为 SystemBarConfigs 的内部类。

1、SystemBarConfig

// 系统条将开始出现在HUN的顶部的z轴顺序
private static final int HUN_ZORDER = 10;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 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 int getSide() {return mSide;}private int getBarType() {return mBarType;}private int getGirth() {return mGirth;}private int getZOrder() {return mZOrder;}private boolean getHideForKeyboard() {return mHideForKeyboard;}private int[] getPaddings() {return mPaddings;}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); // 设置窗口半透明(全透明: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) {// 顶部栏窗口类型为TYPE_STATUS_BAR_ADDITIONAL,底部栏TYPE_NAVIGATION_BAR_PANELreturn 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;}
}

        这里主要用来配置状态栏类型及对应的视图内容,其中状态栏位置和标题等信息是在上面的 populateMaps() 方法中赋值。

populateMaps

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 void populateMaps() {// 将系统栏位置与Gravity值关联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);
}

2、SystemBarConfigBuilder

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);}
}

        SystemBarConfigBuilder 同样是 SystemBarConfigs 的内部类,它实现了构建者模式(Builder Pattern),用于简化 SystemBarConfig 对象的创建。 

 


http://www.ppmy.cn/devtools/151951.html

相关文章

openharmony应用开发快速入门

开发准备 本文档适用于OpenHarmony应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用&#xff08;如下图所示&#xff09;&#xff0c;快速了解工程目录的主要文件&#xff0c;熟悉OpenHarmony应用开发流程。 在开始之前&#xff0c;您需要了解有关OpenHarmon…

Oracle 表空间的使用与创建

Oracle 表空间的使用与创建&#xff08;结合创建用户及权限管理&#xff09; 在Oracle数据库中&#xff0c;表空间是数据库存储的逻辑单位&#xff0c;它用于存储数据库中的数据对象&#xff0c;如表、索引等。Oracle提供了不同类型的表空间&#xff08;如普通表空间、大表空间…

跨站请求伪造(CSRF)介绍

一、什么是跨站请求伪造&#xff08;CSRF&#xff09; 跨站请求伪造&#xff08;Cross-Site Request Forgery&#xff0c;简称CSRF&#xff09;是一种针对网站的恶意利用方式&#xff0c;也被称为“One Click Attack”或“Session Riding”。 CSRF攻击通过伪装来自受信任用户…

java权限修饰符

Java 的访问修饰符&#xff08;Access Modifiers&#xff09;是控制类、方法、变量等成员访问范围的关键工具。它们定义了类、方法或成员变量的可见性&#xff0c;以及在不同位置&#xff08;如不同包或子类&#xff09;是否能访问这些成员。Java 提供了四种主要的访问修饰符&a…

Hadoop•用Web UI查看Hadoop状态词频统计

听说这里是目录哦 通过Web UI查看Hadoop运行状态&#x1f407;一、关闭防火墙二、在物理计算机添加集群的IP映射三、启动集群四、进入HDFS的Web UI 词频统计&#x1f9a9;1、准备文本数据2、在HDFS创建目录3、上传文件4、查看文件是否上传成功5、运行MapReduce程序6、查看MapRe…

ES6中有哪些作用域

在ES6&#xff08;ECMAScript 2015&#xff09;中&#xff0c;作用域&#xff08;Scope&#xff09;是指变量、函数和对象在代码中的可访问范围。ES6 引入了新的作用域规则&#xff0c;除了传统的全局作用域和函数作用域外&#xff0c;还新增了块级作用域。以下是 ES6 中的主要…

STM32 HAL库函数入门指南:从原理到实践

1 STM32 HAL库概述 STM32 HAL(Hardware Abstraction Layer)库是ST公司专门为STM32系列微控制器开发的一套硬件抽象层函数库。它的核心设计理念是在应用层与硬件层之间建立一个抽象层&#xff0c;这个抽象层屏蔽了底层硬件的具体实现细节&#xff0c;为开发者提供了一套统一的、…

vue2:实现上下两栏布局,可拖拽改变高度

要拖拽改变两栏高度,那么总高度要确定,在拖拽的过程中,实时根据光标位置计算两栏高度,所以: 1、最外层有一个box, 高度是屏幕高度screenHeight; 2、该值在页面挂载时获取初始值(window.innerHeight-100),这里减少100,因为窗口上面有工具栏; 3、监听窗口resize事件…