热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

Android5.1的快捷开关的添加和刷新机制和Android6、Android7差不多是一样的。虽然现在Android7.0的快捷开关还没有认真的去研读,只是稍微的看了一下。An

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

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

《Android5.1 快捷开关如何添加和刷新状态》

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


好了言归正传。

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

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



wifi,bt,data,airplane,audioprofile,hotspot,dnd,powersave,rotation,location,flashlight,takescreenshot

关键的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 panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);//找到QSPanel
if (mQSPanel != null) {


//初始化 QSTileHost
final QSTileHost qsh = new QSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, mHotspotController,
mCastController, mFlashlightController,
mUserSwitcherController, mKeyguardMonitor,
mSecurityController,
/// M: add HotKnot in quicksetting
mHotKnotController,
/// M: add AudioProfile in quicksetting
mAudioProfileController
);
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() {
@Override
public void onTilesChanged() {
mQSPanel.setTiles(qsh.getTiles());
}
});
}

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

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

@Override
public Collection> getTiles() {
return mTiles.values();
}

mTiles的类型:

LinkedHashMap> mTiles = new LinkedHashMap<>();


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

@Override
public void onTuningChanged(String key, String newValue) {
if (!TILES_SETTING.equals(key)) {
return;
}
if (DEBUG) Log.d(TAG, "Recreating tiles");
final List tileSpecs = loadTileSpecs(newValue);
if (tileSpecs.equals(mTileSpecs)) return;
for (Map.Entry> tile : mTiles.entrySet()) {
if (!tileSpecs.contains(tile.getKey())) {
if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
tile.getValue().destroy();
}
}
final LinkedHashMap> 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 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 tiles = new ArrayList();
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> 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 removeView(mDeviderViewGroup[i]);
}
mDeviderViewGroup = new View[row];
}
for (int i = 0; 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() {
@Override
public 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 again
visibility = INVISIBLE;
}
setTileVisibility(r.tileView, visibility);
r.tileView.onStateChanged(state);
}
@Override
public void onShowDetail(boolean show) {
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) {
announceForAccessibility(announcement);
}
};
r.tile.setCallback(callback);
final View.OnClickListener click = new View.OnClickListener() {
@Override
public void onClick(View v) {
r.tile.click();
}
};
final View.OnClickListener clickSecOndary= new View.OnClickListener() {
@Override
public void onClick(View v) {
r.tile.secondaryClick();
}
};
final View.OnLongClickListener lOngClick= new View.OnLongClickListener() {
@Override
public 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 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);
}
@Override
public 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去更新图标和文本的。


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 突破MIUI14限制,自定义胶囊图标、大图标样式,支持任意APP
    本文介绍了如何突破MIUI14的限制,实现自定义胶囊图标和大图标样式,并支持任意APP。需要一定的动手能力和主题设计师账号权限或者会主题pojie。详细步骤包括应用包名获取、素材制作和封包获取等。 ... [详细]
  • Imdevelopinganappwhichneedstogetmusicfilebystreamingforplayinglive.我正在开发一个应用程序,需要通过流 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • Java手机看书软件(一)魔屏3.0魔屏(MoScreen)是一款手机漫画阅读器,可以查找、下载阅读多种格式的富媒体内容.魔屏3.0主要功能:漫画分类:支持分类浏览漫画名称、简介、 ... [详细]
  • Androidwifi对象属性及简易Demo本章介绍Android开发中WiFi热点和WiFi属性的获取,介绍WiFi的名称、状态等属性以及获取周围 ... [详细]
  • 转载自http:blog.csdn.netzhifeiyu2008articledetails8829637打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF ... [详细]
author-avatar
沈驰27
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有