AndroidN 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开发者学习的。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值