AndroidN SystemUI新增小图标

本文详细解析了Android SystemUI中的快捷图标实现原理,包括图标如何添加到下拉菜单、图标状态更新机制及自定义图标流程。

每天都要进步一点点

之前分析了SystemUI的主体视图,本篇来点干货,先分析快捷小图标的呈现过程,然后会我们自己新增一个快捷小图标。

SystemUI快捷图标界面

SystemUI快捷图标的UML图如下:
这里写图片描述

Tips:
UML图中,斜体的内容表示抽象,比如handleClick方法是抽象方法,因此其字体为斜体。

对照上图,讲述下快捷小图标是如何被添加到SystemUI的下拉菜单里的。

首先我们关注QSTile类,它是所有快捷图标的父类,一个快捷小图标就是一个Tile,它提供了两个重要的抽象方法:

  • handleClick: 处理小图标的点击事件,比如点击蓝牙就会响应该方法,进而关闭或打开蓝牙。
  • handleUpdateState: 处理小图标的状态更新,高亮表示功能打开。

这里你可能会有疑问,为什么要将handleUpdateState单独抽离成一个方法呢?在handleClick也能去更新小图标状态啊。这里我们需要注意图标的更新不光是通过点击事件,比如我们从Settings里去打开蓝牙,此时并没有走handleClick方法,但蓝牙图标状态也需要去更新。
QSTile类中还定义了Hose接口,它暴露出一些列get图标控制类的方法,比如getBluetoothController/getLocationController等。

接着看下QSTile的子类,我们直观看到的一个个快捷小图标都是直接继承了QSTile,父类不管子小图标如何响应点击事件,如何处理状态更新。但父类却从一个高的视角规范了小图标的必要动作,只要你是一个快捷图标,那么你就必须要能点解,能更换状态。从这我们也看到了抽象的好处。

最后看QSTileHost类,快捷图标有了,那么还需要一个控制器去控制快捷图标的状态,每一个快捷图标对应一个自己的控制器,QSTileHost实现了Host接口,所有的快捷图标控制器都可以通过QSTileHost获取到,同时它提供了创建/添加/删除快捷图标的方法。总之所有快捷小图标的显示隐藏都是它在控制。

以上从点分析分析了快捷图标的主要相关类,我们以BluetoothTile为例,看看Android系统如何将这些点串起来的。

根据之前的博文,我们知道SystemUI的视图创建是从PhoneStatusBar类中makeStatusBarView开始的,查看该方法我们找到了控制器初始化的语句:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

BluetoothControllerImpl mBluetoothController;
protected PhoneStatusBarView makeStatusBarView() {
  // ...
   mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());
   //...
}

然后继续查看该方法,调用SystemUIFactory将各种控制器交给了QSTileHost

BluetoothControllerImpl mBluetoothController;
protected PhoneStatusBarView makeStatusBarView() {
  // ...
   final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext,   this,mBluetoothController,
   mLocationController,mRotationLockController,
   mNetworkController,mZenModeController,
   mHotspotController,mCastController,
   mFlashlightController,mUserSwitcherController,
   mUserInfoController,mKeyguardMonitor,
   mSecurityController,mBatteryController,
   mIconController,mNextAlarmController,mHotKnotController);
   //...
}

SystemUIFactory创建出QSTileHost。
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java

public QSTileHost createQSTileHost(Context context, PhoneStatusBar statusBar,
            BluetoothController bluetooth, LocationController location,
            RotationLockController rotation, NetworkController network,
            ZenModeController zen, HotspotController hotspot,
            CastController cast, FlashlightController flashlight,
            BatterySaverController batterySaverController,
            UserSwitcherController userSwitcher, UserInfoController userInfo,
            KeyguardMonitor keyguard, SecurityController security,
            BatteryController battery, StatusBarIconController iconController,
            NextAlarmController nextAlarmController,
            HotKnotController hotKnotController) {
        return new QSTileHost(context, statusBar, bluetooth, location, rotation, network, zen, hotspot, cast, flashlight, batterySaverController, userSwitcher, userInfo, keyguard, security, battery, iconController, nextAlarmController, hotKnotController);
    }

QSTileHost负责创建/添加/删除快捷图标。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java

public QSTile<?> createTile(String tileSpec) {
    IQuickSettingsPlugin quickSettingsPlugin = PluginManager
            .getQuickSettingsPlugin(mContext);
    if (tileSpec.equals("wifi")) return new WifiTile(this);
    else if (tileSpec.equals("bt")) return new BluetoothTile(this);
    else if (tileSpec.equals("cell")) return new CellularTile(this);
    //...
}

public void addTile(ComponentName tile) {
    List<String> newSpecs = new ArrayList<>(mTileSpecs);
    newSpecs.add(0, CustomTile.toSpec(tile));
    changeTiles(mTileSpecs, newSpecs);
}

public void removeTile(ComponentName tile) {
    List<String> newSpecs = new ArrayList<>(mTileSpecs);
    newSpecs.remove(CustomTile.toSpec(tile));
    changeTiles(mTileSpecs, newSpecs);
}

UML图没有给出createTile是被谁调用的,下面给出其调用栈:

com.android.systemui.statusbar.phone.QSTileHost.onTuningChanged:400
com.android.systemui.tuner.TunerService.addTunable:147
com.android.systemui.tuner.TunerService.addTunable:131
com.android.systemui.statusbar.phone.QSTileHost.<init>:196
com.android.systemui.SystemUIFactory.createQSTileHost:114
com.android.systemui.statusbar.phone.PhoneStatusBar.makeStatusBarView:1002
com.android.systemui.statusbar.phone.PhoneStatusBar.addStatusBarWindow:3608
com.android.systemui.statusbar.phone.PhoneStatusBar.createAndAddWindows:3604
com.android.systemui.statusbar.BaseStatusBar.start:892
com.android.systemui.statusbar.phone.PhoneStatusBar.start:715

最后来到了BluetoothTile,蓝牙相关的具体行为都在该类中实现,比较容易理解,不在赘述。

添加自己的快捷图标

以上分析完了快捷图标的相关过程,可以看到源码的结构是非常清晰,一旦我们理解了一个图标的处理过程,那么当我们自己想添加一个图标,将是非常容易的,下面一起尝试添加一个BatterySaver的快捷图标。我们只给出主要的主题,完整的patch会在最后放出。
1.添加BatterySaverTile

public class BatterySaverTile extends QSTile<QSTile.BooleanState> implements
    BatterySaverController.Listener{
    @Override
    protected void handleUpdateState(BooleanState state, Object arg) {
        state.value = arg instanceof Boolean ? (Boolean) arg
                : mBatterySaverController.isBatterySaverEnabled();
        state.label = "batterysaver";
        state.contentDescription = state.label;
        state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver
                : R.drawable.ic_data_saver_off);
        state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
                = Switch.class.getName();
    }
        @Override
    protected void handleClick() {
        Log.d("azhengye", "BatterySaver click----->");
        toggleBatterySaver();
    }
    //控制
    private void toggleBatterySaver() {
      //具体实现略过
        mState.value = !mBatterySaverController.isBatterySaverEnabled();
        MetricsLogger.action(mContext, getMetricsCategory(), mState.value);
        mBatterySaverController.setBatterySaverEnabled(mState.value);
        refreshState(mState.value);
    }
}

2.添加BatterySaverController

public class BatterySaverController {
    public void setBatterySaverEnabled(boolean isBatterSaverEnabled){
        //do you need todo
    }

    public boolean isBatterySaverEnabled() {
        return true;
    }
    public interface Listener{
        void onBatterySaverChanged(boolean isBatterSaver);
    }
}

3.创建BatterySaverController实例
PhoneStatusBar中new出BatterySaverController实例,并将其传给SystemUIFactory.getInstance().createQSTileHost

4.QSTileHost添加创建BatterySaverTile语句

进过上述主要步骤,新增的图标就出现了,如下图:

这里写图片描述

更具体的代码实现,我做成了一个patch文件,请去该处获取。
patch 文件只是一个非常简单的demo,并没有真正的实现BatterSaver功能

Tips:
下载patch文件后,直接进入SystemUI根目录执行git am 0001-add-new-tile-demo.patch即可看到修改

总结

SystemUI模块在对快捷菜单的实现上,其代码架构组织的非常良好,好的架构让后人在其基础上拓展是非常开心的一件事。本文只是选取了其中一部分容易理解的点,整个SystemUI的代码架构都是非常值得Android开发者学习的。

<think>我们正在处理一个关于修改Android 14 SystemUI中通知图标大小的问题。根据用户提供的引用,我们可以参考一些相关的修改经验,比如引用[2]中修改导航栏图标大小的例子,引用[5]中修改锁屏图标大小的例子。虽然这些不是直接修改通知图标,但思路类似:找到控制图标大小的代码位置,然后调整大小值。 在SystemUI中,通知图标通常位于状态栏(StatusBar)和通知面板(NotificationPanel)中。我们需要找到控制这些图标大小的资源文件或代码。 步骤: 1. 确定通知图标在SystemUI中的位置:状态栏和通知面板中的通知图标可能由不同的类控制。常见的类包括`StatusBarIconView`(状态栏图标)和`NotificationIconContainer`(通知面板中的图标区域)。 2. 查找图标大小的定义:图标大小可能在dimens.xml文件中定义,也可能在代码中动态计算。 3. 修改图标大小:根据找到的位置,调整大小值。 参考引用[2]中导航栏图标的修改,我们发现图标大小在矢量图的width和height属性中定义(如28dp)。而引用[5]中锁屏图标的大小修改则是在代码中通过scaleFactor进行缩放。 对于通知图标,我们可能需要修改两个地方:状态栏中的图标大小和通知面板中的图标大小。 具体修改: 1. 状态栏通知图标大小: - 状态栏图标通常由`StatusBarIconView`类控制。图标大小可能在`dimens.xml`中定义,例如查找`status_bar_icon_size`或类似名称的dimen值。 - 在代码中,我们也可以找到设置图标大小的位置。例如,在`StatusBarIconView`的`setScale`方法或构造函数中。 2. 通知面板中的通知图标大小: - 通知面板中的图标通常在`NotificationIconContainer`中管理。同样,我们可以在`dimens.xml`中查找相关定义,如`notification_icon_size`等。 根据Android版本的不同,具体位置可能有所变化。在Android 14中,我们可以尝试以下路径: - 状态栏图标大小:在`res/values/dimens.xml`中,查找`status_bar_icon_drawing_size`或`status_bar_icon_size`。 - 通知面板图标大小:同样在`dimens.xml`中,查找`notification_icon_size`或`notification_small_icon_size`。 如果dimens.xml中没有,则需要在代码中查找。例如: - 状态栏图标:查看`StatusBarIconView`类,可能在初始化时设置了固定大小。 - 通知面板图标:查看`NotificationIconContainer`或相关的视图类。 修改示例(以dimens.xml为例): ```xml <!-- 修改状态栏通知图标大小 --> <dimen name="status_bar_icon_drawing_size">24dp</dimen> <!-- 修改通知面板中的图标大小 --> <dimen name="notification_icon_size">48dp</dimen> ``` 注意:修改后需要重新编译SystemUI并刷入设备。 另外,引用[1]提到状态栏透明效果有内存限制,但我们的修改是图标大小,不涉及透明效果,所以不受此限制影响。 如果用户希望动态调整大小(如根据屏幕密度),可以参考引用[5]中的缩放方法,在代码中动态计算。 由于我们无法直接访问Android 14的源码,以上路径和名称是基于历史版本和常见命名的推测。具体实现时,需要根据实际代码进行调整。 相关问题: 1. 如何编译和部署修改后的SystemUI? 2. 除了修改图标大小,如何调整通知图标的间距? 3. Android 14中SystemUI的其他自定义修改有哪些?</think>### Android 14 SystemUI 通知图标大小修改指南 要修改 Android 14 中 SystemUI 的通知图标大小,需要调整相关资源文件和代码逻辑。以下是具体步骤: #### 1. **修改图标尺寸资源** 通知图标大小通常在 `dimens.xml` 中定义。查找并修改以下资源值: ```xml <!-- 文件路径: packages/SystemUI/res/values/dimens.xml --> <dimen name="status_bar_notification_icon_size">24dp</dimen> <!-- 默认值 --> <dimen name="notification_mini_icon_size">16dp</dimen> <!-- 折叠状态栏图标 --> ``` 将数值调整为所需大小(如 `28dp`),注意保持比例协调[^2][^5]。 #### 2. **调整图标容器布局** 在 `NotificationIconContainer` 相关布局中修改约束条件: ```xml <!-- 文件路径: packages/SystemUI/res/layout/status_bar.xml --> <com.android.systemui.statusbar.views.NotificationIconContainer android:id="@+id/notificationIcons" android:layout_width="wrap_content" android:layout_height="@dimen/status_bar_icon_size" android:minWidth="@dimen/status_bar_notification_icon_size" /> ``` 增加 `minWidth` 和 `minHeight` 确保图标有足够显示空间[^2][^4]。 #### 3. **修改动态缩放逻辑** 在 `StatusBarIconView` 类中调整缩放因子: ```java // 文件路径: packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java public class StatusBarIconView extends AnimatedImageView { private void updateScale() { // 修改此处的缩放系数 (0.8f 改为 1.0f 可取消缩放) setScaleX(0.8f); setScaleY(0.8f); } } ``` 移除或增大缩放值可扩大图标显示区域[^5]。 #### 4. **调整通知栏图标间距** 修改图标间距资源值: ```xml <!-- 文件路径: packages/SystemUI/res/values/dimens.xml --> <dimen name="notification_icon_padding">2dp</dimen> <dimen name="status_bar_system_icon_spacing">4dp</dimen> ``` 增大这些值可避免大图标重叠[^2]。 #### 5. **处理折叠状态栏图标** 小尺寸图标在 `NotificationShelf` 中单独定义: ```java // 文件路径: packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java protected void updateSmallIconAlpha() { // 查找此方法中的尺寸引用 int size = getResources().getDimensionPixelSize( R.dimen.notification_mini_icon_size); mStatusBarIcon.setIconSize(size); } ``` #### 验证步骤: 1. 修改后编译 SystemUI 模块:`m SystemUI` 2. 推送更新到设备:`adb push $OUT/system_ext/priv-app/SystemUI /system_ext/priv-app/` 3. 重启 SystemUI:`adb shell pkill com.android.systemui` 4. 发送测试通知验证图标尺寸变化 > ⚠️ 注意:修改系统组件需要系统签名,建议在 AOSP 环境下编译完整系统镜像进行测试[^1][^4]。 --- ### 相关问题 1. 如何修改 Android 14 状态栏电池图标样式? 2. SystemUI 修改后如何避免系统自动恢复默认值? 3. 调整通知图标大小时如何保持与其他系统图标的视觉平衡? 4. Android 14 中如何自定义通知面板的背景透明度? 5. 修改 SystemUI 资源后出现 OTA 更新冲突如何解决? [^1]: 在 Android Kitkat 之后 Google 加入了原生的状态栏透明的效果 [^2]: Android 12.0 导航栏 Icon 图标大小修改 [^4]: Android 源码学习------SystemUI(二) [^5]: Android 锁屏图标的大小修改
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值