SystemUI 7.0学习笔记三-QSPanel
来源:互联网 发布:iel数据库网址 编辑:程序博客网 时间:2024/05/17 01:33
QSPanel加载流程
首先通过一张图了解一下,如图红色区域就是QSPanel:包含亮度条和Quick Settings;本节的重点是后者。
QSPanel是StatusBar的一部分,自然是随着StatusBar的加载而加载,SystemUI 7.0学习笔记二-状态栏和导航栏介绍了状态栏的加载实在PhoneStatusBar的makeStatusBarView()中完成的;其实,这个方法完成了很多systemui重要组件的加载工作。再来回顾一下代码。
PhoneStatusBar.makeStatusBarView():
protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; inflateStatusBarWindow(context); ...... // 这里完成了许多systemui关键组件的view创建 ...... // Set up the quick settings tile panel AutoReinflateContainer container = (AutoReinflateContainer) mStatusBarWindow.findViewById( R.id.qs_auto_reinflate_container); if (container != null) { final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, mHotspotController, mCastController, mFlashlightController, mUserSwitcherController, mUserInfoController, mKeyguardMonitor, mSecurityController, mBatteryController, mIconController, mNextAlarmController); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow); container.addInflateListener(new InflateListener() { @Override public void onInflated(View v) { QSContainer qsContainer = (QSContainer) v.findViewById( R.id.quick_settings_container); qsContainer.setHost(qsh); mQSPanel = qsContainer.getQsPanel(); mQSPanel.setBrightnessMirror(mBrightnessMirrorController); mKeyguardStatusBar.setQSPanel(mQSPanel); mHeader = qsContainer.getHeader(); initSignalCluster(mHeader); mHeader.setActivityStarter(PhoneStatusBar.this); } }); } ......}
这里发现了mQSPanel,它是在QSContainer的onFinishInflate()中inflate的,接着SystemUIFactory通过createQSTileHost()调用new QSTileHost(…)创建了QSTileHost对象。
QSTileHost的继承关系及构造方法:
public class QSTileHost implements QSTile.Host, Tunable { public QSTileHost(Context context, PhoneStatusBar statusBar, ...) { ...... TunerService.get(mContext).addTunable(this, TILES_SETTING); }}
注意:这里的TunerService是在SystemUI学习总结一-SystemUI的启动中和SystemBars等服务一起在SystemUIApplication开启的。
看到SERVICES 就想起来了吧!
private final Class<?>[] SERVICES = new Class[] { com.android.systemui.tuner.TunerService.class, com.android.systemui.keyguard.KeyguardViewMediator.class, com.android.systemui.recents.Recents.class, com.android.systemui.volume.VolumeUI.class, Divider.class, com.android.systemui.statusbar.SystemBars.class, com.android.systemui.usb.StorageNotification.class, com.android.systemui.power.PowerUI.class, com.android.systemui.media.RingtonePlayer.class, com.android.systemui.keyboard.KeyboardUI.class, com.android.systemui.tv.pip.PipUI.class, com.android.systemui.shortcut.ShortcutKeyDispatcher.class, com.android.systemui.VendorServices.class };
好,言归正传。
继续看TunerService.addTunable(),
public void addTunable(Tunable tunable, String... keys) { for (String key : keys) { addTunable(tunable, key); } } private void addTunable(Tunable tunable, String key) { if (!mTunableLookup.containsKey(key)) { mTunableLookup.put(key, new ArraySet<Tunable>()); } mTunableLookup.get(key).add(tunable); 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); // 回调到QSTileHost tunable.onTuningChanged(key, value); }
上面代码中的value值为:
“airplane,wifi,cell,flashlight,bt,hotspot,location,rotation,battery,audioprofile,screenshots”这样的字符串,用于后面生成具体的QSTile。
QSTileHost.onTuningChanged():
@Override public void onTuningChanged(String key, String newValue) { if (!TILES_SETTING.equals(key)) { return; } if (DEBUG) Log.d(TAG, "Recreating tiles"); if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) { newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode); } final List<String> tileSpecs = loadTileSpecs(mContext, newValue); // 返回分割好的QSTile名字list int currentUser = ActivityManager.getCurrentUser(); if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return; for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) { if (!tileSpecs.contains(tile.getKey())) { if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey()); tile.getValue().destroy(); } } final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>(); for (String tileSpec : tileSpecs) { QSTile<?> tile = mTiles.get(tileSpec); if (tile != null && (!(tile instanceof CustomTile) || ((CustomTile) tile).getUser() == currentUser)) { if (DEBUG) Log.d(TAG, "Adding " + tile); tile.removeCallbacks(); if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) { tile.userSwitch(currentUser); } newTiles.put(tileSpec, tile); } else { if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec); try { tile = createTile(tileSpec); // 根据名字创建具体的QSTile if (tile != null && tile.isAvailable()) { tile.setTileSpec(tileSpec); newTiles.put(tileSpec, tile); } } catch (Throwable t) { Log.w(TAG, "Error creating tile for spec: " + tileSpec, t); } } } mCurrentUser = currentUser; mTileSpecs.clear(); mTileSpecs.addAll(tileSpecs); mTiles.clear(); mTiles.putAll(newTiles); for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onTilesChanged(); } }
loadTileSpecs():
protected List<String> loadTileSpecs(Context context, String tileList) { final Resources res = context.getResources(); final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); if (tileList == null) { tileList = res.getString(R.string.quick_settings_tiles); if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList); } else { if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList); } final ArrayList<String> tiles = new ArrayList<String>(); boolean addedDefault = false; for (String tile : tileList.split(",")) { tile = tile.trim(); if (tile.isEmpty()) continue; if (tile.equals("default")) { if (!addedDefault) { tiles.addAll(Arrays.asList(defaultTileList.split(","))); addedDefault = true; } } else { tiles.add(tile); } } return tiles; }
查看代码发现,如果传进来的tileList为空,就会从SystemUI的资源文件中去读取默认的tiles字符串。其实,SystemUI第一次的时候确实是从自己的资源文件中读取的quick_settings_tiles_default,只有编辑过tiles后才会将tiles字符串写入settings_secure.xml。
接着看createTile():
public QSTile<?> createTile(String tileSpec) { 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); else if (tileSpec.equals("dnd")) return new DndTile(this); else if (tileSpec.equals("inversion")) return new ColorInversionTile(this); else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this); else if (tileSpec.equals("work")) return new WorkModeTile(this); else if (tileSpec.equals("rotation")) return new RotationLockTile(this); else if (tileSpec.equals("flashlight")) return new FlashlightTile(this); else if (tileSpec.equals("location")) return new LocationTile(this); else if (tileSpec.equals("cast")) return new CastTile(this); else if (tileSpec.equals("hotspot")) return new HotspotTile(this); else if (tileSpec.equals("user")) return new UserTile(this); else if (tileSpec.equals("battery")) return new BatteryTile(this); else if (tileSpec.equals("saver")) return new DataSaverTile(this); else if (tileSpec.equals("night")) return new NightDisplayTile(this); else if (tileSpec.equals("nfc")) return new NfcTile(this); // Intent tiles. else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec); else { Log.w(TAG, "Bad tile spec: " + tileSpec); return null; } }
到这里,具体的tiles都创建好了,可是还是没看到怎么加到QSPanel的。让我们回到本文开始的地方
PhoneStatusBar.makeStatusBarView():
qsContainer.setHost(qsh);
QSTileHost的构造函数中完成了具体的tiles,将它的对象set进来肯定有用,相信这里能够找到答案。
QSContainer.setHost(qsh):
public void setHost(QSTileHost qsh) { mQSPanel.setHost(qsh, mQSCustomizer); //mHeader(StatusBarHeaderView) QSPanel加入到了StatusBarHeaderView中 mHeader.setQSPanel(mQSPanel); mQSDetail.setHost(qsh); mQSAnimator.setHost(qsh); }
接着看QSPanel.setHost(qsh):
public void setHost(QSTileHost host, QSCustomizer customizer) { mHost = host; mHost.addCallback(this); setTiles(mHost.getTiles()); mFooter.setHost(host); mCustomizePanel = customizer; if (mCustomizePanel != null) { mCustomizePanel.setHost(mHost); } mBrightnessController.setBackgroundLooper(host.getLooper()); }
好像发现了什么,对,就是mHost.getTiles()。
接着看QSPanel.setTiles(tiles):
public void setTiles(Collection<QSTile<?>> tiles) { setTiles(tiles, false); } 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) { // 在这里,具体的每个QStitle在这里被addTile进了QSPanel addTile(tile, collapsedView); } }
QSPanel.addTile(tile):
protected void 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; final View.OnClickListener click = new View.OnClickListener() { @Override public void onClick(View v) { onTileClick(r.tile); } }; final View.OnLongClickListener longClick = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { r.tile.longClick(); return true; } }; r.tileView.init(click, longClick); r.tile.refreshState(); mRecords.add(r); if (mTileLayout != null) { mTileLayout.addTile(r); } }
上面addTile()代码中创建了一个Callback,实际运行中Title变化刷新,点击事件等许多操作都将与这个Callback挂钩。
mTileLayout(PagedTileLayout)是QSPanel的child,
protected void setupTileLayout() { mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate( R.layout.qs_paged_tile_layout, this, false); mTileLayout.setListening(mListening); addView((View) mTileLayout); }
PagedTileLayout.addTile(r)调用流程:
@Override public void addTile(TileRecord tile) { mTiles.add(tile); postDistributeTiles(); } private void postDistributeTiles() { removeCallbacks(mDistribute); post(mDistribute); } private final Runnable mDistribute = new Runnable() { @Override public void run() { distributeTiles(); } }; private void distributeTiles() { if (DEBUG) Log.d(TAG, "Distributing tiles"); final int NP = mPages.size(); for (int i = 0; i < NP; i++) { mPages.get(i).removeAllViews(); } int index = 0; final int NT = mTiles.size(); for (int i = 0; i < NT; i++) { TileRecord tile = mTiles.get(i); if (mPages.get(index).isFull()) { if (++index == mPages.size()) { if (DEBUG) Log.d(TAG, "Adding page for " + tile.tile.getClass().getSimpleName()); mPages.add((TilePage) LayoutInflater.from(mContext) .inflate(R.layout.qs_paged_page, this, false)); } } if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " + index); mPages.get(index).addTile(tile); // add tile } if (mNumPages != index + 1) { mNumPages = index + 1; while (mPages.size() > mNumPages) { mPages.remove(mPages.size() - 1); } if (DEBUG) Log.d(TAG, "Size: " + mNumPages); mPageIndicator.setNumPages(mNumPages); mDecorGroup.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE); setAdapter(mAdapter); mAdapter.notifyDataSetChanged(); setCurrentItem(0, false); } }
mPages的每一页都是TilePage,TilePage的父类是TileLayout:
private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
public static class TilePage extends TileLayout { ......}
接着看TileLayout.addTile():
public void addTile(TileRecord tile) { mRecords.add(tile); tile.tile.setListening(this, mListening); addView(tile.tileView); // 加载tile视图 }
至此,本文就告一段落了。
参考文章
- http://blog.csdn.net/qq_27215521/article/details/62888832
- http://blog.csdn.net/Otaku_627/article/details/53573040
- http://www.jianshu.com/p/502ac67d78b7
- SystemUI 7.0学习笔记三-QSPanel
- SystemUI 7.0学习笔记四-Recents
- systemui 学习笔记一
- SystemUI源码分析四(QSPanel显示的流程)
- SystemUI 7.0学习笔记二-状态栏和导航栏
- SystemUI 7.0学习总结一-SystemUI的启动
- android6.0 SystemUI之快捷设置区域QSPanel及点击事件流程分析
- android6.0 SystemUI之快捷设置区域QSPanel及点击事件流程分析
- systemUI学习
- SystemUi音量显示笔记
- SystemUI PoweUI笔记
- SystemUI笔记 SystemBars
- SystemUI 5.0 学习日记
- Android 7.0之systemUI
- SystemUI
- SystemUI
- [学习记录]SystemUI
- [学习记录]SystemUI
- String与hashCode方法
- JAVA05
- Java多线程中FutureTask详解与正式环境问题定位
- 未完成
- 《JavaScript语言精粹》学习笔记
- SystemUI 7.0学习笔记三-QSPanel
- 炸群脚本-消息轰炸
- 题二 朋友
- android中的进程间通信
- 尼克切斯定理(难度:2颗星)
- 安卓学习笔记--- Android 沉浸式状态栏的实现方法,状态栏透明实现
- 《计算机程序设计艺术》pdf.zip
- cf 412 C. Success Rate
- C++初始化列表与构造函数异同