赞
踩
在上一个博文(Android SystemUI之StatusBar,状态栏(二))的开篇有给出一个图,里面描述了StatusBar的设备树。super_status_bar会分两个分支一个是状态栏,这个上个博文已经讲了,另一个就是下拉菜单,QS面板。也是本博文需要讲解的。
在说下拉菜单创建的过程我们先看两副图
下拉菜单两种不同的布局,现在我们就来好好分析这两个布局的创建流程。
status_bar_expanded.xml
- <com.android.systemui.statusbar.phone.NotificationPanelView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/notification_panel"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/transparent" >
-
- <include
- layout="@layout/keyguard_status_view"
- android:visibility="gone" />
-
- <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:id="@+id/notification_container_parent"
- android:clipToPadding="false"
- android:clipChildren="false">
- <!--
- layout_gravity="@integer/notification_panel_layout_gravit-> center_horizontal|top
-
- -->
- <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" />
- <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
- android:id="@+id/notification_stack_scroller"
- android:layout_marginTop="@dimen/notification_panel_margin_top"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="match_parent"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:layout_marginBottom="@dimen/close_handle_underlap" />
- <include layout="@layout/ambient_indication"
- android:id="@+id/ambient_indication_container" />
- <ViewStub
- android:id="@+id/keyguard_user_switcher"
- android:layout="@layout/keyguard_user_switcher"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
- <include
- layout="@layout/keyguard_status_bar"
- android:visibility="invisible" />
- <Button
- android:id="@+id/report_rejected_touch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
- android:text="@string/report_rejected_touch"
- android:visibility="gone" />
- </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
- <include
- layout="@layout/keyguard_bottom_area"
- android:visibility="gone" />
- <com.android.systemui.statusbar.AlphaOptimizedView
- android:id="@+id/qs_navbar_scrim"
- android:layout_height="96dp"
- android:layout_width="match_parent"
- android:layout_gravity="bottom"
- android:visibility="invisible"
- android:background="@drawable/qs_navbar_scrim" />
- </com.android.systemui.statusbar.phone.NotificationPanelView>
这个status_bar_expanded.xml就是下拉菜单的布局文件。里面包含的View很多,我们主要看以下几个:
1.@layout/keyguard_status_view 这个是锁屏界面的View
2.@+id/qs_frame QS快捷面板
3.@+id/notification_stack_scroller短信通知栏
在StatusBar有如下这段代码,这样@+id/qs_frame的界面的控制就被转移到QSFragment,相应的layout也就变成了qs_panel
- //快捷面板
- View container = mStatusBarWindow.findViewById(R.id.qs_frame);
- if (container != null) {
- FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
- ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
- Dependency.get(ExtensionController.class)
- .newExtension(QS.class)
- .withPlugin(QS.class)
- .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
- .withDefault(QSFragment::new)
- .build());
- final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
- mIconController);
- mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
- (visible) -> {
- mBrightnessMirrorVisible = visible;
- updateScrimController();
- });
- 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);
- }
- });
- }
- <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:clipToPadding="false"
- android:clipChildren="false" >
-
- <!-- Main QS background -->
- <View
- android:id="@+id/quick_settings_background"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:elevation="4dp"
- android:background="@drawable/qs_background_primary" />
-
- <!-- Black part behind the status bar -->
- <View
- android:id="@+id/quick_settings_status_bar_background"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/quick_qs_offset_height"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:background="#ff000000" />
-
- <!-- Gradient view behind QS -->
- <View
- android:id="@+id/quick_settings_gradient_view"
- android:layout_width="match_parent"
- android:layout_height="126dp"
- android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:background="@drawable/qs_bg_gradient" />
-
-
- <com.android.systemui.qs.QSPanel
- android:id="@+id/quick_settings_panel"
- android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/qs_footer_height"
- android:elevation="4dp"
- android:background="@android:color/transparent"
- android:focusable="true"
- android:accessibilityTraversalBefore="@id/qs_carrier_text"
- />
-
- <include layout="@layout/quick_status_bar_expanded_header" />
-
- <include layout="@layout/qs_footer_impl" />
-
- <include android:id="@+id/qs_detail" layout="@layout/qs_detail" />
-
- <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
- android:visibility="gone" />
-
- </com.android.systemui.qs.QSContainerImpl>
1.@+id/quick_settings_panel 这个就是快捷面板容器,布局风格对应我们开篇说的第一幅图。
2.@layout/quick_status_bar_expanded_header 这个layout也包含了一个快捷面板,布局风格对应我们开篇说的第二幅图
- public void setHost(QSTileHost qsh) {
- mQSPanel.setHost(qsh, mQSCustomizer);
- mHeader.setQSPanel(mQSPanel);
- mFooter.setQSPanel(mQSPanel);
- mQSDetail.setHost(qsh);
-
- if (mQSAnimator != null) {
- mQSAnimator.setHost(qsh);
- }
- }
- public void setHost(QSTileHost host, QSCustomizer customizer) {
-
- mHost = host;
- mHost.addCallback(this);
- setTiles(mHost.getTiles());
-
- mFooter.setHostEnvironment(host);
- mCustomizePanel = customizer;
- if (mCustomizePanel != null) {
- mCustomizePanel.setHost(mHost);
- }
- mQuickSettingsExt.setHostAppInstance(host);
- }
setTiles(mHost.getTiles())这个就是快捷面板添加的入口,那思考一下,mHost.getTiles()这个数据是从哪里获取的呢?
答案是:QSTileHost.onTuningChanged
- 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);
- int currentUser = ActivityManager.getCurrentUser();
- if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
- mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
- tile -> {
- 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 (tile.isAvailable()) {
- if (DEBUG) Log.d(TAG, "Adding " + tile);
- tile.removeCallbacks();
- if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
- tile.userSwitch(currentUser);
- }
- newTiles.put(tileSpec, tile);
- } else {
- tile.destroy();
- }
- } else {
- if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
- try {
- tile = createTile(tileSpec);
- if (tile != null) {
- if (tile.isAvailable()) {
- tile.setTileSpec(tileSpec);
- newTiles.put(tileSpec, tile);
- } else {
- tile.destroy();
- }
- }
- } 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();
- }
- }
- protected List<String> loadTileSpecs(Context context, String tileList) {
- final Resources res = context.getResources();
- String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
- /// M: Customize the quick settings tile order for operator. @{
- if (mQuickSettingsExt != null) {
- defaultTileList = mQuickSettingsExt.addOpTileSpecs(defaultTileList);
- // @}
- defaultTileList = mQuickSettingsExt.customizeQuickSettingsTileOrder(defaultTileList);
- }
- if(StatusBar.SYSTEMUI_NOTIFICATION_DEBUG)Log.i(StatusBar.TAG_XIAO,"QSTilHost loadTileSpecs defaultTileList:"+ defaultTileList);
- /// M: Customize the quick settings tile order for operator. @}
- Log.d(TAG, "loadTileSpecs() default tile list: " + defaultTileList);
- 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;
- }
- public QSTile createTile(String tileSpec) {
- for (int i = 0; i < mQsFactories.size(); i++) {
- QSTile t = mQsFactories.get(i).createTile(tileSpec);
- if (t != null) {
- return t;
- }
- }
- // M: @ {
- if (mQuickSettingsExt != null && mQuickSettingsExt.doOperatorSupportTile(tileSpec)) {
- // WifiCalling
- return (QSTile) mQuickSettingsExt.createTile(this, tileSpec);
- }
- // @ }
- return null;
- }
QSFactoryImpl.createTile
- public QSTile createTile(String tileSpec) {
- QSTileImpl tile = createTileInternal(tileSpec);
- if (tile != null) {
- tile.handleStale(); // Tile was just created, must be stale.
- }
- return tile;
- }
-
- private QSTileImpl createTileInternal(String tileSpec) {
- /// M: Add extra tiles in quicksetting @{
- Context context = mHost.getContext();
- IQuickSettingsPlugin quickSettingsPlugin = OpSystemUICustomizationFactoryBase
- .getOpFactory(context).makeQuickSettings(context);
- /// @}
- // Stock tiles.
- switch (tileSpec) {
- case "wifi":
- return new WifiTile(mHost);
- case "bt":
- return new BluetoothTile(mHost);
- case "cell":
- return new CellularTile(mHost);
- case "dnd":
- return new DndTile(mHost);
- case "inversion":
- return new ColorInversionTile(mHost);
- case "airplane":
- return new AirplaneModeTile(mHost);
- case "work":
- return new WorkModeTile(mHost);
- case "rotation":
- return new RotationLockTile(mHost);
- case "flashlight":
- return new FlashlightTile(mHost);
- case "location":
- return new LocationTile(mHost);
- case "cast":
- return new CastTile(mHost);
- case "hotspot":
- return new HotspotTile(mHost);
- case "user":
- return new UserTile(mHost);
- case "battery":
- return new BatterySaverTile(mHost);
- case "saver":
- return new DataSaverTile(mHost);
- case "night":
- return new NightDisplayTile(mHost);
- case "nfc":
- return new NfcTile(mHost);
- }
-
- /// M: Customize the quick settings tiles for operator. @{
- if (tileSpec.equals("dataconnection") && !SIMHelper.isWifiOnlyDevice())
- return new MobileDataTile(mHost);
- else if (tileSpec.equals("simdataconnection") && !SIMHelper.isWifiOnlyDevice() &&
- quickSettingsPlugin.customizeAddQSTile(new SimDataConnectionTile(mHost)) != null) {
- return (SimDataConnectionTile) quickSettingsPlugin.customizeAddQSTile(
- new SimDataConnectionTile(mHost));
- } else if (tileSpec.equals("dulsimsettings") && !SIMHelper.isWifiOnlyDevice() &&
- quickSettingsPlugin.customizeAddQSTile(new DualSimSettingsTile(mHost)) != null) {
- return (DualSimSettingsTile) quickSettingsPlugin.customizeAddQSTile(
- new DualSimSettingsTile(mHost));
- } else if (tileSpec.equals("apnsettings") && !SIMHelper.isWifiOnlyDevice() &&
- quickSettingsPlugin.customizeAddQSTile(new ApnSettingsTile(mHost)) != null) {
- return (ApnSettingsTile) quickSettingsPlugin.customizeAddQSTile(
- new ApnSettingsTile(mHost));
- }
- /// @}
- // Intent tiles.
- if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(mHost, tileSpec);
- if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
-
- // Debug tiles.
- if (Build.IS_DEBUGGABLE) {
- if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
- return new GarbageMonitor.MemoryTile(mHost);
- }
- }
-
- // Broken tiles.
- Log.w(TAG, "Bad tile spec: " + tileSpec);
- return null;
- }
onTuningChanged这个函数的创建比QSFragment更早,所以当我们调用mHost.getTiles()时,数据就已经准备好了。在loadTileSpecs函数里面有这一行 String defaultTileList = res.getString(R.string.quick_settings_tiles_default)获取我们需要加载在快捷面板上面的项目。依据defaultTileList 来createTile(tileSpec)创建对应的QSTile。至此数据创建完毕,后续就是数据的使用
- public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
- if (!collapsedView) {
- mQsTileRevealController.updateRevealedTiles(tiles);
- }
- //先清空再加载
- for (TileRecord record : mRecords) {
- mTileLayout.removeTile(record);
- record.tile.removeCallback(record.callback);
- }
- mRecords.clear();
- for (QSTile tile : tiles) {
- addTile(tile, collapsedView);
- }
- }
- 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);//加载到页面上
- }
-
- return r;
- }
- protected QSTileView createTileView(QSTile tile, boolean collapsedView) {
- return mHost.createTileView(tile, collapsedView);
- }
- public QSTileView createTileView(QSTile tile, boolean collapsedView) {
- for (int i = 0; i < mQsFactories.size(); i++) {
- QSTileView view = mQsFactories.get(i).createTileView(tile, collapsedView);
- if (view != null) {
- return view;
- }
- }
- throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
- }
QSFactoryImpl.createTileView
- public QSTileView createTileView(QSTile tile, boolean collapsedView) {
- Context context = new ContextThemeWrapper(mHost.getContext(), R.style.qs_theme);
- QSIconView icon = tile.createTileView(context);
- if (collapsedView) {
- return new QSTileBaseView(context, icon, collapsedView);
- } else {
- return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
- }
- }
这段代码的工作有两个:1.由tile的数据创建QSTileView,并且保持在TileRecord。
2.把创建好的TileRecord 添加的快捷面板中 mTileLayout.addTile(r)。
mTileLayout是什么?先看下面一段代码
- public QSPanel(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
-
- setOrientation(VERTICAL);//设置竖直方向
-
- mBrightnessView = LayoutInflater.from(mContext).inflate(
- R.layout.quick_settings_brightness_dialog, this, false);
- addView(mBrightnessView);
-
- // M: @ {
- mQuickSettingsExt = OpSystemUICustomizationFactoryBase
- .getOpFactory(context).makeQuickSettings(context);
- if (mQuickSettingsExt != null) {
- mQuickSettingsExt.addOpViews(this);
- }
- // @ }
-
- mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
- R.layout.qs_paged_tile_layout, this, false);
- mTileLayout.setListening(mListening);
- addView((View) mTileLayout);
-
- mPanelPageIndicator = (PageIndicator) LayoutInflater.from(context).inflate(
- R.layout.qs_page_indicator, this, false);
- addView(mPanelPageIndicator);
-
- ((PagedTileLayout) mTileLayout).setPageIndicator(mPanelPageIndicator);
- mQsTileRevealController = new QSTileRevealController(mContext, this,
- (PagedTileLayout) mTileLayout);
-
- addDivider();
-
- mFooter = new QSSecurityFooter(this, context);
- addView(mFooter.getView());
-
- updateResources();
-
- mBrightnessController = new BrightnessController(getContext(),
- findViewById(R.id.brightness_icon),
- findViewById(R.id.brightness_slider));
- }
从上面代码我们看两个关键信息:1.亮度调节显示控件在此被动态加载成功mBrightnessView = LayoutInflater.from(mContext).inflate(
R.layout.quick_settings_brightness_dialog, this, false);
2.mTileLayout 其实是由qs_paged_tile_layout的layout加载而来,mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate( R.layout.qs_paged_tile_layout, this, false);
- <com.android.systemui.qs.PagedTileLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
-
- <FrameLayout
- android:id="@+id/page_decor"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_gravity="bottom">
-
- </FrameLayout>
-
- </com.android.systemui.qs.PagedTileLayout>
PagedTileLayout是一个ViewPager,我们熟悉ViewPager的用法,那么我们也就不难推断出PagedTileLayout的一些特性。比如滑动翻页。
继续之前mTileLayout.addTile(r)的代码研究
- public void addTile(TileRecord tile) {
- mTiles.add(tile);
- postDistributeTiles();
- }
private final ArrayList<TileRecord> mTiles = new ArrayList<>();是一个list,就是把数据保存起来
- 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(getContext())
- .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);
- }
- 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);
- setAdapter(mAdapter);
- mAdapter.notifyDataSetChanged();
- setCurrentItem(0, false);
- }
- }
上面的代码中有一个比较关键的 mPages.get(index).addTile(tile);,
mPages是 private final ArrayList<TilePage> mPages = new ArrayList<>();
由于index=0.这也就导致只有第一页才会加载所有的快捷图标,实际我们使用也是这样的。
那我们来看看TilePage是什么
- public static class TilePage extends TileLayout {
- private int mMaxRows = 3;
- public TilePage(Context context, AttributeSet attrs) {
- super(context, attrs);
- updateResources();
- }
-
- @Override
- public boolean updateResources() {
- final int rows = getRows();
- boolean changed = rows != mMaxRows;
- if (changed) {
- mMaxRows = rows;
- requestLayout();
- }
- return super.updateResources() || changed;
- }
-
- private int getRows() {
- //快捷面板显示的行数
- return Math.max(1, getResources().getInteger(R.integer.quick_settings_num_rows));
- }
-
- public void setMaxRows(int maxRows) {
- mMaxRows = maxRows;
- }
-
- public boolean isFull() {
- return mRecords.size() >= mColumns * mMaxRows;
- }
- }
继承了TileLayout,并且对于滑动的页数是由getRows()来决定的
TileLayout 我们看三个函数,因为这三个函数跟快捷按键的图标大小位置有关。
1.updateResources 会初始化一些值,比如高度和边距等。
2.onMeasure 主要确定TileLayout的宽度和高度,也就是快捷面板的宽高,并且确定子View的宽高
3.onLayout确定子View的布局排列,是一行排三个还是一行排四个,都是在onLayout里面来实现。
- 捷面板里面的每个单元格图标的显示大小以及间隔
- public boolean updateResources() {
- final Resources res = mContext.getResources();
- final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
- mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
- mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
- mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
- mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
- mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
- if (mColumns != columns) {
- mColumns = columns;
- requestLayout();
- return true;
- }
- return false;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int numTiles = mRecords.size();
- final int width = MeasureSpec.getSize(widthMeasureSpec)
- - getPaddingStart() - getPaddingEnd();
- final int numRows = (numTiles + mColumns - 1) / mColumns;//每行的数量
- mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;//每个图标的宽度
-
- // Measure each QS tile.
- View previousView = this;
- for (TileRecord record : mRecords) {
- if (record.tileView.getVisibility() == GONE) continue;
- record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));//测量子View的宽度和高度
- previousView = record.tileView.updateAccessibilityOrder(previousView);
- }
-
- // Only include the top margin in our measurement if we have more than 1 row to show.
- // Otherwise, don't add the extra margin buffer at top.
- //最后计算出高度大小,
- int height = (mCellHeight + mCellMarginVertical) * numRows +
- (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
- if (height < 0) height = 0;
- setMeasuredDimension(width, height);
- }
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int w = getWidth();
- final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- int row = 0;
- int column = 0;
- // Layout each QS tile.
- //最后决定子View的位置在哪里
- for (int i = 0; i < mRecords.size(); i++, column++) {
- // If we reached the last column available to layout a tile, wrap back to the next row.
- if (column == mColumns) {
- column = 0;
- row++;
- }
- final TileRecord record = mRecords.get(i);
- final int top = getRowTop(row);
- final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
- final int right = left + mCellWidth;
- record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
- }
- }
所以到现在我们的下拉菜单中的快捷面板的加载流程已经加载完成。至于短信的加载流程暂时不讲,等以后有时间再研究。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。