Android 5.1的快捷开关的添加和刷新机制和Android6、Android7差不多是一样的。虽然现在Android7.0的快捷开关还没有认真的去研读,只是稍微的看了一下。Android6.0和Android7.0相比较于Android5.0的快捷开关增加了不少的代码。增加的代码主要是增加了下拉快捷面板的时候其中的一些动画,还有就是Android7.0中增加了快捷开关的自定义排序功能。但是万变不离其宗。下面切入到正题。

先看一下定制的快捷开关的UI样式。

快捷面板上面有很多的快捷开关,但是其中各个快捷开关的是如何运作的还不是很清楚。

好了言归正传。

添加快捷开关首先需要去配置文件config.xml文件中去添加快捷开关的名称。

config.xml文件的路径是:frameworks/base/packages/SystemUI/res/values/config.xml中。其关键代码如下:

<!-- The default tiles to display in QuickSettings --><string name="quick_settings_tiles_default" translatable="false">wifi,bt,data,airplane,audioprofile,hotspot,dnd,powersave,rotation,location,flashlight,takescreenshot</string>

关键的java代码主要是在: frameworks/base/packages/SystemUI/com/android/systemui/qs 下面,包括QSPanel.java、

QSTile.java、QSTileView.java等。

在手机启动的时候,首先会去加载和创建和初始化PhoneStatusBar.java (frameworks/base/packages/SystemUI/com/android/systemui/statusbar/phone/)中的控件和类。当然也包括快捷面板的

初始化操作。PhoneStatusBar中关于快捷面板的关键代码如下:

// Set up the quick settings tile panelmQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);//找到QSPanelif (mQSPanel != null) {

     //初始化 QSTileHostfinal QSTileHost qsh = new QSTileHost(mContext, this,mBluetoothController, mLocationController, mRotationLockController,mNetworkController, mZenModeController, mHotspotController,mCastController, mFlashlightController,mUserSwitcherController, mKeyguardMonitor,mSecurityController,/// M: add HotKnot in quicksettingmHotKnotController,/// M: add AudioProfile in quicksettingmAudioProfileController);mQSPanel.setHost(qsh);

     //通过QSTileHost获得的各个快捷开关(qsh.getTiles())加入到QSPanel中。mQSPanel.setTiles(qsh.getTiles());
 
     //初始化调节屏幕亮度控制器mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);mQSPanel.setBrightnessMirror(mBrightnessMirrorController);mHeader.setQSPanel(mQSPanel);qsh.setCallback(new QSTileHost.Callback() {@Overridepublic void onTilesChanged() {mQSPanel.setTiles(qsh.getTiles());}});}

通过跟踪上面的代码,我们可以看到 需要通过QSTileHost去获取快捷开关(qsh.getTiles())。那么,qsh.getTiles()

是如何去获取到各个快捷开关的呢?我们一起去看看吧。打开QSTileHost.java类。找到getTiles()方法。

 @Overridepublic Collection<QSTile<?>> getTiles() {return mTiles.values();}

mTiles的类型:

LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>();

找到mTiles是在哪赋值的,通过查找可以找到是在下面的这个类中。

@Overridepublic void onTuningChanged(String key, String newValue) {if (!TILES_SETTING.equals(key)) {return;}if (DEBUG) Log.d(TAG, "Recreating tiles");final List<String> tileSpecs = loadTileSpecs(newValue);if (tileSpecs.equals(mTileSpecs)) 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) {if (mTiles.containsKey(tileSpec)) {newTiles.put(tileSpec, mTiles.get(tileSpec));} else {if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);try {newTiles.put(tileSpec, createTile(tileSpec));} catch (Throwable t) {Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);}}}mTileSpecs.clear();mTileSpecs.addAll(tileSpecs);mTiles.clear();mTiles.putAll(newTiles);if (mCallback != null) {mCallback.onTilesChanged();}}

上面最主要的两个方法是loadTileSpecs() 方法和 createTile()方法。那我们看看loadTileSpecs方法的具体作用是什么。

//loadTileSpecs() 此方法主要是去之前配置好的config.xml文件中获取各个快捷开关的名称。然后拆分字符串。

 protected List<String> loadTileSpecs(String tileList) {final Resources res = mContext.getResources();//获取Config.xml中配置需要显示的快捷开关的名称。String defaultTileList = res.getString(R.string.quick_settings_tiles_default);// M: Add extra tiles @{defaultTileList += "," + res.getString(R.string.quick_settings_tiles_extra);//default// @}/// M: Customize the quick settings tile order for operator. @{IQuickSettingsPlugin quickSettingsPlugin = PluginFactory.getQuickSettingsPlugin(mContext);defaultTileList = quickSettingsPlugin.customizeQuickSettingsTileOrder(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;}

//createTile() 方法的作用是 创建各个快捷开关的对象。

 private QSTile<?> createTile(String tileSpec) {if (tileSpec.equals("wifi") && !WCN_DISABLED) return new WifiTile(this);else if (tileSpec.equals("bt") && !WCN_DISABLED) return new BluetoothTile(this);else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);else if (tileSpec.equals("cell")) return new CellularTile(this);else if (tileSpec.equals("data")) return new DataConnectionTile(this);else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
//        else if (tileSpec.equals("lte") && TelephonyManagerSprd.isDeviceSupportLte()) return new LteServiceTile(this);
//        else if (tileSpec.equals("lte") && isDeviceSupportLte()) return new LteServiceTile(this);else if (tileSpec.equals("rotation")) return new RotationLockTile(this);else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);else if (tileSpec.equals("location") && !WCN_DISABLED) 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("diabolo")) return new DiaboloTile(this);else if (tileSpec.equals("dnd")) return new DndTile(this);else if (tileSpec.equals("audioprofile") ) //&& SIMHelper.isMtkAudioProfilesSupport()return new AudioProfileTile(this);
//        else if (tileSpec.equals("interruption")) return new InterruptionsTile(this);else if (tileSpec.equals("powersave")) return new PowerSaveTile(this);else if (tileSpec.equals("takescreenshot")) return new TakeScreenShotTile(this);else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);}

好了,经过上面的分析,终于知道配置文件的作用。知道使如何创建的了之后,但是还没有完。下面来看看他是如何

刷新和更新快捷开关的图标和文本的。

我们还是需要从上面的 PhoneStateBar.java 中的 代码中继续往下分析,要分析的代码如下:

//通过QSTileHost获得的各个快捷开关(qsh.getTiles())加入到QSPanel中。mQSPanel.setTiles(qsh.getTiles());

关键的是QSPanel.java类中的setTiles()方法。

public void setTiles(Collection<QSTile<?>> tiles) {for (TileRecord record : mRecords) {removeView(record.tileView);}mRecords.clear();int row = tiles.size() / mColumns + 1;if (mDeviderViewGroup == null) {mDeviderViewGroup = new View[row];} else {for (int i = 0; i < mDeviderViewGroup.length; i++) {removeView(mDeviderViewGroup[i]);}mDeviderViewGroup = new View[row];}for (int i = 0; i < row; i++) {addDividerLine(i);}for (QSTile<?> tile : tiles) {addTile(tile);}if (isShowingDetail()) {mDetail.bringToFront();}}

上面setTiles()方法中,需要注意的是addTile()方法。如果打开QSPanel.java 类,我们会知道QSPanel类

继承于ViewGroup的。下面我们在来看看addTile()方法的做了写什么。

private void addTile(final QSTile<?> tile) {final TileRecord r = new TileRecord();r.tile = tile;r.tileView = tile.createTileView(mContext);r.tileView.setVisibility(View.GONE);final QSTile.Callback callback = new QSTile.Callback() {@Overridepublic void onStateChanged(QSTile.State state) {if (state.visible && !mGridContentVisible) {// We don't want to show it if the content is hidden,// then we just set it to invisible, to ensure that it gets// visible againvisibility = INVISIBLE;}setTileVisibility(r.tileView, visibility);r.tileView.onStateChanged(state);}@Overridepublic void onShowDetail(boolean show) {QSPanel.this.showDetail(show, r);}@Overridepublic void onToggleStateChanged(boolean state) {if (mDetailRecord == r) {fireToggleStateChanged(state);}}@Overridepublic void onScanStateChanged(boolean state) {r.scanState = state;if (mDetailRecord == r) {fireScanStateChanged(r.scanState);}}@Overridepublic void onAnnouncementRequested(CharSequence announcement) {announceForAccessibility(announcement);}};r.tile.setCallback(callback);final View.OnClickListener click = new View.OnClickListener() {@Overridepublic void onClick(View v) {r.tile.click();}};final View.OnClickListener clickSecondary = new View.OnClickListener() {@Overridepublic void onClick(View v) {r.tile.secondaryClick();}};final View.OnLongClickListener longClick = new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {r.tile.longClick();return true;}};r.tileView.init(click, clickSecondary, longClick);r.tile.setListening(mListening);callback.onStateChanged(r.tile.getState());r.tile.refreshState();mRecords.add(r);addView(r.tileView);}

我们可以看到addTile()方法的最后面是调用的addView方法。说明是将各个快捷开关添加到QSPanel的ViewGroup当中。

成为QSPanel的子控件。

下面我们在来看看 子控件。这里以手电筒(FlashlightTile.java)为例来说明其实如何来刷新图标和文本的。

//FlashlightTile.java

public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
FlashlightController.FlashlightListener {
 ......
 @Override  protected void handleClick() {  if (ActivityManager.isUserAMonkey()) {  return; }   MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);    boolean newState = !mState.value;  refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);   mFlashlightController.setFlashlight(newState);  Settings.System.putInt(mContext.getContentResolver(), "open_flashlight_by_quicksetting_panel", newState ? 1 : 0); // 1 打开 0 关闭    }
 .......
}

FlashlightTile.java 中用来更新手电筒图标和文本的代码是refreshState() 去刷新。当调用refreshState的时候

会去调用QSTile.java类中的freshState(Object arg)方法。

 protected final void refreshState(Object arg) {mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();}
protected final class H extends Handler {private static final int SET_CALLBACK = 1;private static final int CLICK = 2;private static final int SECONDARY_CLICK = 3;private static final int LONG_CLICK = 4;private static final int REFRESH_STATE = 5;private static final int SHOW_DETAIL = 6;private static final int USER_SWITCH = 7;private static final int TOGGLE_STATE_CHANGED = 8;private static final int SCAN_STATE_CHANGED = 9;private static final int DESTROY = 10;private H(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {String name = null;try {if (msg.what == SET_CALLBACK) {name = "handleSetCallback";handleSetCallback((QSTile.Callback)msg.obj);} else if (msg.what == CLICK) {name = "handleClick";mAnnounceNextStateChange = true;handleClick();} else if (msg.what == SECONDARY_CLICK) {name = "handleSecondaryClick";handleSecondaryClick();} else if (msg.what == LONG_CLICK) {name = "handleLongClick";handleLongClick();} else if (msg.what == REFRESH_STATE) {name = "handleRefreshState";handleRefreshState(msg.obj);} else if (msg.what == SHOW_DETAIL) {name = "handleShowDetail";handleShowDetail(msg.arg1 != 0);} else if (msg.what == USER_SWITCH) {name = "handleUserSwitch";handleUserSwitch(msg.arg1);} else if (msg.what == TOGGLE_STATE_CHANGED) {name = "handleToggleStateChanged";handleToggleStateChanged(msg.arg1 != 0);} else if (msg.what == SCAN_STATE_CHANGED) {name = "handleScanStateChanged";handleScanStateChanged(msg.arg1 != 0);} else if (msg.what == DESTROY) {name = "handleDestroy";handleDestroy();} else {throw new IllegalArgumentException("Unknown msg: " + msg.what);}} catch (Throwable t) {final String error = "Error in " + name;Log.w(TAG, error, t);mHost.warn(error, t);}}}
 protected void handleRefreshState(Object arg) {handleUpdateState(mTmpState, arg);final boolean changed = mTmpState.copyTo(mState);if (changed) {handleStateChanged();}}

跟踪代码,最终发现调用FlashlightTile.java中的refreState()方法,最后会调用QSTile.java的handleRefreshState

方法。handleRefreshState方法又会调用handleUpdateState和handleStateChanged() 两个方法。handleUpdateState

方法会去调用QSPanel的子控件FlashlightTile.java中的handleUpdateState方法。去重新获得图标和文本。最后调用

handleStateChanged去回调QSpanel.java 中的代码。

private void handleStateChanged() {boolean delayAnnouncement = shouldAnnouncementBeDelayed();if (mCallback != null) {mCallback.onStateChanged(mState);if (mAnnounceNextStateChange && !delayAnnouncement) {String announcement = composeChangeAnnouncement();if (announcement != null) {mCallback.onAnnouncementRequested(announcement);}}}mAnnounceNextStateChange = mAnnounceNextStateChange && delayAnnouncement;}

上边标记的 橘黄色文本的地方所示,回去调用QSPanel.java中橘黄色代码的地方(当上文中描述QSPanel代码的地方

查看)。最终会调用r.tileView.onStateChanged(state)

r.tileView.onStateChanged(state);

这段代码会去调用QSTileView.java 中的onStateChanged方法。自己去查看onStateChanged源码的话,发现

其实际上是会根据传进来的参数state去更新图标和文本的。

Android5.1 快捷开关如何添加和刷新状态相关推荐

  1. SystemUI添加第三方快捷开关时灰显问题分析

    作者:neek.chen Android7.0上不仅有默认的快捷快关(QSTile),也允许客户自行添加第三方快捷开关(CustomTile).本文只介绍在绑定第三方CustomTile时遇到的间歇性 ...

  2. Android 9.0 SystemUI 下拉状态栏快捷开关

    SystemUI 下拉状态栏快捷开关是 QSPanel,qs_panel.xml,@+id/quick_settings_panel,本篇文章就来看看这些快捷开关是如何呈现的以及如何新增一个快捷开关? ...

  3. android 获取快捷开关_干货水帖:Android 6.0 如何从快速开关面板启动微信扫一扫?...

    自从 iOS 开放了 Widget 接口之后,很多「勇士」就探索 Widget 的各种可能性.比如前段时间很火的 Steve,让你不开锁屏就能玩到 Chrome 中内置的恐龙跳小游戏. iOS 的 W ...

  4. Android通知栏增加快捷开关的技术实现

    我们通常可以在通知栏上看到"飞行模式"."移动数据"."屏幕录制"等开关按钮,这些按钮都属于通知栏上的快捷开关,点击快捷开关可以轻易调用某种 ...

  5. 微信状态听歌怎么添加歌曲 微信状态听歌如何设置网易云音乐

    微信状态听歌最近又被大家玩火了,在前天,网易云和微信来了波梦幻联动,网易云音乐能在微信上直接播放了,那么,微信状态听歌怎么添加歌曲? 微信状态听歌如何设置网易云音乐?下面就一起来看看吧. 微信状态听歌 ...

  6. android4.4 添加快捷开关(以截屏为例)

    A,frameworks\base\packages\SystemUI\res\values\config.xml 添加: <bool name="quick_settings_sho ...

  7. Android 8.0 SystemUI下拉状态栏快捷开关

    基于工作需要,基本是在Android源生代码上进行开发,从android 5.0到现在8.0,这两年碰到各种问题发现关于Android源生发开方面的特别少.于是想着开始把遇到的.解决的问题写下来,或许 ...

  8. Extjs4 tab添加右键刷新功能

    根据DEMO带的TabCloseMenu修改而成 1 1.在开头增加enableRerfesh: true,配置项2 3 //用于设置刷新按钮是否可用,刷新按钮通过extraItemsTail添加, ...

  9. EasyUI 表格点击右键添加或刷新 绑定右键菜单

    例1 在HTML页面中设置一个隐藏的菜单(前提是已经使用封装的Easyui) 代码: <div id="contextMenu_jygl" class="easyu ...

  10. webpack配置---设置快捷打包和浏览器自动刷新

    1.设置快捷打包 找到package.json中scripts 之后再输入命令npm run build 2.设置浏览器自动刷新 要先安装html-webpack-plugin这个插件再配置 3.将c ...

最新文章

  1. PAT(甲级)2018年冬季考试 7-4 Heap Paths(非递归与递归解法)
  2. linux每天一小步---sed命令详解
  3. sql server存储过程中解决单引号的问题
  4. Objectc 基础类型
  5. 计算机内部总线和外部总线,科学网-怎样将计算机内部总线扩展为外部网络?-姜咏江的博文...
  6. 安徽医科大学计算机考研,这所985院校将现场面试!安徽医科大学缺额超800人!调剂信息更新...
  7. 谁在使用Docker?
  8. 算法分析与设计实验报告——实现哈夫曼编码
  9. 系统集成(IBMS)软件的功能
  10. seo入门级教程!再看不懂就放弃做互联网吧!
  11. 记 flip 简单的动画思路
  12. 《50个教育法:我把三个儿子送入了斯坦福》书中的精髓:了解教育的本质,以言传身教、耐心引导的教育方式培养孩子成才。
  13. java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal
  14. 【微信小程序】图片上传组件“mp-uploader“(weui)
  15. 网站seo中期的操作
  16. 使用ZRender绘制表格
  17. supervised——>self-supervised
  18. 微信零钱交易记录怎么彻底删除,这些方法等你来盘
  19. 加速下载工具motrix
  20. 记录一种ssh旧系统和前后端分离新系统结合的解决方案,以及两系统间通过token进行认证的方式

热门文章

  1. Windows---diskpart命令的使用
  2. 如何锻炼提高自己的逻辑思维?这里给你7个方法!
  3. MyBatis:choose标签的用法
  4. 机械硬盘显示数据错误循环冗余检查文件如何寻回
  5. 疯狂夹娃娃机源码+教程
  6. matlab彩色图像处理实验报告,matlab简单图像处理实验报告
  7. html语言制作折线图,html5绘制折线图
  8. 基于jQuery的图片懒加载插件
  9. 3d游戏项目实训一周总结
  10. html网页设计优秀作品和代码,从优秀的网页设计作品中学排版和配色