快捷设置区域的布局是由StatusBar.java的makeStatusBarView()统一加载;
protected void makeStatusBarView() {
........
inflateStatusBarWindow(context);//加载布局的方法
........
}
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
}
super_status_bar.xml
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
status_bar_expanded.xml
<FrameLayout
android:id="@+id/qs_frame"
android:layout="@layout/qs_panel"
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
qs_panel.xml
<com.android.systemui.qs.QSContainerImpl
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
android:clipToPadding="false"
android:clipChildren="false">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
android:layout_marginTop="@dimen/qs_panel_margin_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp" />
......
</com.android.systemui.qs.QSContainerImpl>
见名知意,这个id为quick_settings_panel即为我们所找的那个SystemUI上的快捷设置区域控件的ID;
代码控制方面
仍然是从StatusBar.java的makeStatusBarView()方法开始
protected void makeStatusBarView() {
.......
// Set up the quick settings tile panel
View container = mStatusBarWindow.findViewById(R.id.qs_frame);//获取视图对象
if (container != null) {
......
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mIconController);//创建并初始化QSTileHost对象
mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
mScrimController);
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
if (qs instanceof QSFragment) {
((QSFragment) qs).setHost(qsh);
mQSPanel = ((QSFragment) qs).getQsPanel();
mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
mKeyguardStatusBar.setQSPanel(mQSPanel);
}
});
.......
}
一步步分析,首先先实例化一个View(qs_frame包含一个QSPanel视图)对象,然后再去创建QSTileHost对象,看一下QSTileHost的构造方法
QSTileHost.java
public QSTileHost(Context context, StatusBar statusBar,
StatusBarIconController iconController) {
mIconController = iconController;
mContext = context;
mStatusBar = statusBar;
// M: @ {
mQuickSettingsExt = OpSystemUICustomizationFactoryBase
.getOpFactory(context).makeQuickSettings(context);
// @ }
mServices = new TileServices(this, Dependency.get(Dependency.BG_LOOPER));
mQsFactories.add(new QSFactoryImpl(this));
Dependency.get(PluginManager.class).addPluginListener(this, QSFactory.class, true);
Dependency.get(TunerService.class).addTunable(this, TILES_SETTING);//使用TunerService去Settings中查询key为TILES_SETTING的值,即查询快捷设置菜单项
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
mAutoTiles = new AutoTileManager(context, this);
}
关键Dependency.get(TunerService.class).addTunable(this, TILES_SETTING);使用TunerService去Settings中查询key为TILES_SETTING的值,即查询快捷设置菜单项,查询到的结果通过onTuningChanged()方法回调返回;
TunerServiceImpl.java
private void addTunable(Tunable tunable, String key) {
if (!mTunableLookup.containsKey(key)) {
mTunableLookup.put(key, new ArraySet<Tunable>());
}
mTunableLookup.get(key).add(tunable);
if (LeakDetector.ENABLED) {
mTunables.add(tunable);
Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
}
Uri uri = Settings.Secure.getUriFor(key);
if (!mListeningUris.containsKey(uri)) {
mListeningUris.put(uri, key);
mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
}
// Send the first state.
String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
tunable.onTuningChanged(key, value);//查询到的结果通过onTuningChanged()方法回调返回
}
public abstract class TunerService {
.......
public interface Tunable {
void onTuningChanged(String key, String newValue);
}
......
}
QSTileHost实现接口Tunable,实现方法onTuningChanged
@Override
public void onTuningChanged(String key, String newValue) {
......
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);//调用loadTileSpecs方法将config.xml的内容load进来
......
tile = createTile(tileSpec);//在这里根据String生成Tile
......
newTiles.put(tileSpec,tile);
......
mTiles.putAll(newTiles);
......
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();//这里回调onTilesChanged()方法,通知StatusBar.java对快捷设置选项显示更新
}
}
并将其保存在成员变量的mTiles集合中,最后回调onTilesChanged()方法,通知QSPanel.java对快捷设置选项显示更新。
onTilesChanged()方法的实现是在QSPanel.java中重写的:
@Override
public void onTilesChanged() {
setTiles(mHost.getTiles());
}
至此QSTileHost.java的构造方法分析完成,然后再回到调用处StatusBar.java的makeStatusBarView()方法继续分析:
((QSFragment) qs).setHost(qsh);
QSFragment.java
public void setHost(QSTileHost qsh) {
mQSPanel.setHost(qsh, mQSCustomizer);//
mHeader.setQSPanel(mQSPanel);
mFooter.setQSPanel(mQSPanel);
mQSDetail.setHost(qsh);
}
QSPanel.java
public void setHost(QSTileHost host, QSCustomizer customizer) {
mHost = host;
mHost.addCallback(this);
setTiles(mHost.getTiles());
}
而其中的setTiles()方法会先remove掉所有的TileRecord记录并移除所有的tileView
public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
for (TileRecord record : mRecords) {
mTileLayout.removeTile(record);
record.tile.removeCallback(record.callback);
}
mRecords.clear();
for (QSTile tile : tiles) {
addTile(tile, collapsedView);
}
}
然后在重新调用addTile()创建TileRecord对象并赋值绑定相应的回调和点击事件(点击、双击、长按)接口,再将其保存到ArrayList<TileRecord> mRecords集合中,然后再去addView();
protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
final TileRecord r = new TileRecord();
r.tile = tile;
r.tileView = createTileView(tile, collapsedView);
final QSTile.Callback callback = new QSTile.Callback() {
@Override
public void onStateChanged(QSTile.State state) {
drawTile(r, state);
}
@Override
public void onShowDetail(boolean show) {
// Both the collapsed and full QS panels get this callback, this check determines
// which one should handle showing the detail.
if (shouldShowDetail()) {
QSPanel.this.showDetail(show, r);
}
}
@Override
public void onToggleStateChanged(boolean state) {
if (mDetailRecord == r) {
fireToggleStateChanged(state);
}
}
@Override
public void onScanStateChanged(boolean state) {
r.scanState = state;
if (mDetailRecord == r) {
fireScanStateChanged(r.scanState);
}
}
@Override
public void onAnnouncementRequested(CharSequence announcement) {
if (announcement != null) {
mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
.sendToTarget();
}
}
};
r.tile.addCallback(callback);
r.callback = callback;
r.tileView.init(r.tile);
r.tile.refreshState();
mRecords.add(r);
if (mTileLayout != null) {
mTileLayout.addTile(r);//mTileLayout是layout视图对象
}
return r;
}
protected void setupTileLayout() {
mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
R.layout.qs_paged_tile_layout, this, false);
mTileLayout.setListening(mListening);
addView((View) mTileLayout);//把视图加入view
}