android N 在Settings中作了一些调整,如上面的截图。
首先来看下Settings的Dashboard category,dashboard的中文意思指的是仪表板,在Settings中指的是Settings中显示的选项,如WLAN,Bluetooth这样的,参见上面的预览图片。
在android M中,dashboard的加载是放在SettingsActivity中,而且Settings/res/xml/dashboard_categories.xml这个文件专门用来描述dashboard的整体结构,参见下图。
在Settings N中,则将dashboard这部分的逻辑抽取了出来,放在/frameworks/base/packages/SettingsLib/目录下。N中不再使用dashboard_categories.xml这个文件来描述Settings各选项的架构,而且将Dashboard的初始化放在SettingsLib中来处理,首先看下面的图片:
SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
在TileUtils中定义的Actions,用于标记Activity属于哪一个Dashboard category
/**
* Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
*/
private static final String SETTINGS_ACTION =
"com.android.settings.action.SETTINGS";
private static final String OPERATOR_SETTINGS =
"com.android.settings.OPERATOR_APPLICATION_SETTING";
private static final String OPERATOR_DEFAULT_CATEGORY =
"com.android.settings.category.wireless";
private static final String MANUFACTURER_SETTINGS =
"com.android.settings.MANUFACTURER_APPLICATION_SETTING";
private static final String MANUFACTURER_DEFAULT_CATEGORY =
"com.android.settings.category.device";
Categories定义在Settings/res/values/donottranslate.xml中,分为四个大的Category,如下代码
Settings/res/values/donottranslate.xml
com.android.settings.category.wireless
com.android.settings.category.device
com.android.settings.category.personal
com.android.settings.category.system
TileUtils.java中定义的Meta Data
Name of the meta-data item that should be set in the AndroidManifest.xml
to specify the icon、the title、the summary that should be displayed for the preference.
public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
Meta data会在AndroidManifest.xml进行配置,在TileUtils.java中加载Dashboard Category的时候,会通过PackageManager获得各个Activity的信息后,再动态的更新到页面上。(另外,我发现对于这些Dashboard的icon,title和Summary有的在AndroidManifest.xml中有配置meta-data有的却没有,我感觉这里应该用的是Activity节点下的icon,title(lablel),这部分如果要彻底搞清楚需要看PackageManager解析AndroidManifest.xml的逻辑,这里不作深究)。
接下来看在TileUtils.java代码中是对于Dashboard是如何处理的
上面的getCategories方法主要分为两个部分来看,首先通过PackageManager获得各个Category的信息保存到ArrayList中,接着对ArrayList中的数据按照优先级进行排序,这样主界面拿到这些数据就可以显示了。
看上面这幅图,从上而下分别是Condition,Suggestion和各个显示的Item项。
接下来我们来看这部分在代码中是如何构建的?
private void recountItems() {
reset();
boolean hasConditions = false;
for (int i = 0; mConditions != null && i < mConditions.size(); i++) {
boolean shouldShow = mConditions.get(i).shouldShow();
hasConditions |= shouldShow;
//(1)condition_card.xml
countItem(mConditions.get(i), R.layout.condition_card, shouldShow, NS_CONDITION);
}
boolean hasSuggestions = mSuggestions != null && mSuggestions.size() != 0;
//(2)dashboard_spacer.xml
countItem(null, R.layout.dashboard_spacer, hasConditions && hasSuggestions, NS_SPACER);
//(3)suggestion_header.xml
countItem(null, R.layout.suggestion_header, hasSuggestions, NS_SPACER);
resetCount();
if (mSuggestions != null) {
int maxSuggestions = getDisplayableSuggestionCount();
for (int i = 0; i < mSuggestions.size(); i++) {
//(3)suggestion_tile.xml
countItem(mSuggestions.get(i), R.layout.suggestion_tile, i < maxSuggestions,
NS_SUGGESTION);
}
}
resetCount();
for (int i = 0; mCategories != null && i < mCategories.size(); i++) {
DashboardCategory category = mCategories.get(i);
//(4)dashboard_category.xml
countItem(category, R.layout.dashboard_category, mIsShowingAll, NS_ITEMS);
for (int j = 0; j < category.tiles.size(); j++) {
Tile tile = category.tiles.get(j);
//(5)dashboard_tile.xml
countItem(tile, R.layout.dashboard_tile, mIsShowingAll
|| ArrayUtils.contains(DashboardSummary.INITIAL_ITEMS,
tile.intent.getComponent().getClassName()), NS_ITEMS);
}
}
notifyDataSetChanged();
}
recountItems方法会在构建布局的时候多次调用,这个方法里面会在这里加入多个layout布局文件。
如上面的代码的注释标明部分:
//(1)condition_card.xml
//(2)dashboard_spacer.xml
//(3)suggestion_header.xml
//(4)dashboard_category.xml
//(5)dashboard_tile.xml
这里使用countItem方法将各个布局加入到List中去,分别是下面三个集合
private final List
在将这些布局文件加入到List中去后,然后在onBindViewHolder去获取List中的内容,从而展示在页面上,这部分的逻辑就不再介绍了,大家有兴趣的可以去看看。
N中的Settings使用DrawerLayout为Settings界面加入了侧滑菜单的功能。我们对比下M平台和N平台的Settings Activity的结构就大概明白了。
android N在在SettingsActivity上面构建了一个SettingsDrawerActivity,侧滑的功能则是在SettingsDrawerActivity中实现的,SettingsActivity位于SettingsLib下面。
接下来我们看看SettingsDrawerActivity这个类:
在SettingsDrawerActivity的onCreate方法中会加载settings_with_drawer这个文件。这个文件则是对左侧Drawer的布局文件的描述。如下code:
接着来看左侧Drawer的ListView的数据是如何加载的,这部分的逻辑由SettingsDrawerAdapter来实现。
如上截图,在SettingsDrawerAdapter的updateCategories方法中,添加最上面的home的图片和文件后,然后遍历装有DashboardCategory的集合,取出里面的DashboardCategory和其中的Tile存放到对应的集合中去,用于显示到页面上去。
7.0中的Settings加入的Condition可以显示设置有些item的状态,并且提供快捷开关,在单击后,可以跳转到相应的Settings 页面。
在上文介绍DashboardCategory的整体布局的时候,介绍了Condition部分加载的文件是condition_card.xml文件
如上图和xml文件相对应,分别表明了各个控件的id。
condition_card.xml
我们拿AirplaneModeCondition来举例,在Settings的AndroidManifest.xml中注册了如下的Receiver:
默认情况下这些Condition是关闭的,即enabled的。在这个Receiver中,会去接收这个广播,当Condition的状态改变的时候会去更新状态。
//AirplaneModeCondition.java
@Override
public void refreshState() {
setActive(WirelessUtils.isAirplaneModeOn(mManager.getContext()));
}
public static class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
ConditionManager.get(context).getCondition(AirplaneModeCondition.class)
.refreshState();
}
}
}
suggestion能够在设置的主页面显示一些建议项,相当于为一些常用的功能界面添加入口,用户通过点击这些建议项可以跳到相应的页面进行操作,并且用户可以手动移除这些建议项。
如下面的截图,Suggestion页面分为两个
suggestion_header.xml和suggestion_tile.xml两个布局组成。
关于Suggestion的配置信息:
Suggestion默认的数量为2个,如上图所示,这个常量的设置是在DashboardAdapter.java里面。
private static final int DEFAULT_SUGGESTION_COUNT = 2;
另外这些Suggestion是以一种顺序来显示的,这个部分的配置是在suggestion_ordering.xml中配置的。
这里会通过SuggestionParser.java中new出来的SuggestionOrderInflater来解析这个文件,Suggestion相关的很多解析操作都是由SuggestionParser.java来处理的。
这个类中定义的常量会在Settings的AndroidManifest.xml使用。
如上图中定义的com.android.settings.require_feature的meta-data节点表示该Suggestion的显示需要特定的feature支持,对于FingerprintEnrollSuggestionActivity这个Suggestion的显示则需要指纹的支持。
另外对于META_DATA_DISMISS_CONTROL则控制着当前Suggestion的显示时机。正如上面截图的注释描述。
Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
For instance:
0,10
Will appear immediately, but if the user removes it, it will come back after 10 days.
Another example:
10,30
Will only show up after 10 days, and then again after 30.
这个属性允许Suggestion在特定的天数后显示,并且在被拒绝后重新显示。
0,10表示该Suggestion会立即显示,但是如果用户删除后,会在10天后再次显示。
10,30则表示在10天后显示,然后在30天之后再次显示。
以上就是对于android7.0Settings的一些新功能的分析,其实这部分还有很多东西没有详细地去分析,这部分只是做了简单的介绍。
另外,再去看源码的时候,发现Google的设计真的是厉害,而且自己很多时候都是从源码的功能去理解,其实从架构,性能方面考虑,源码都是非常优秀的,有很多值得学习的地方。