整体结构
Android 4.4系统的设置应用与常规的apk代码结构一致,没有后续android版本中各种新架构的复杂,即严格的activity界面逻辑与xml文件布局对应,每个功能页面都能比较轻松的找到对应的界面与布局代码。
- 入口
按照常规的apk开发经验,我们可以从源码的AndroidManifest.xml文件里通过搜索android.intent.category.LAUNCHER关键字找到对应的主入口activity:
<activity android:name="Settings"
android:label="@string/settings_label_launcher"
android:taskAffinity="com.android.settings"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.settings.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
打开Settings.java可以发现这正是一个activity逻辑代码,对应着点击设置应用打开的那个主界面,其继承PreferenceActivity,以列表形式展现子功能项,而整个页面的生成又几乎与一个普通列表的写法一样,因此不得不说4.4版本的设置在写法上极为友善。
2.主界面分析
对于常规activity界面,必然是去找xml布局来帮助理解,这里由于继承自PreferenceActivity自然通过搜索onBuildHeaders找到:
public void onBuildHeaders(List<Header> headers) {
if (!onIsHidingHeaders()) {
loadHeadersFromResource(R.xml.settings_headers, headers);
updateHeaderList(headers);
}
}
其布局文件在res/下面找到settings_headers.xml,阅读其代码片可以发现其中每一项header正好与主界面列表项对应且标有注释。
而作为一个列表显示,这些header是每一项的数据,那么UI上是如何完成的呢,列表一般自然是通过构建Adapter来控制每一项的UI:
private static class HeaderAdapter extends ArrayAdapter<Header> {
static final int HEADER_TYPE_CATEGORY = 0;
static final int HEADER_TYPE_NORMAL = 1;
static final int HEADER_TYPE_SWITCH = 2;
static final int HEADER_TYPE_BUTTON = 3;
这里的HeaderAdapter编写的也比较容易理解,将子项的Ui显示分成几个类别,用switch case语句匹配各自的显示,具体可以阅读其中的getView方法。
3.点击事件
对于每一项Header,比如我点击wifi,页面就将跳转至wifi界面,这里由实现onHeaderClick方法进行:
public void onHeaderClick(Header header, int position) {
boolean revert = false;
if (header.id == R.id.account_add) {
revert = true;
}
super.onHeaderClick(header, position);
if (revert && mLastHeader != null) {
highlightHeader((int) mLastHeader.id);
} else {
mLastHeader = header;
}
}
这里可以看到实际上就是回调了父类的onHeaderClick,而在父类PreferenceActivity的onHeaderClick里,逻辑会使用header的fragment信息通过startWithFragment生成子功能UI。
4.自定义修改-删除项
在日常工作中对于Settings的定制增加某个功能项,隐藏现有的某项较为常见,通过刚刚的内容可以知道主界面依照严格的activtiy与xml形式。
那么比如我要隐藏掉 打印 这个功能,直接在settings_headers.xml删除对应header会引起编译错误,设置visibility无效,因此比较好的做法是在Settings.java里通过逻辑代码来隐藏。
在updateHeaderList方法里可以阅读逻辑理解其对header列表数据的控制,那么删除某项就比较简单,仿照着写:
else if (id == R.id.date_time_settings) {
target.remove(i);
}
5.自定义修改-增加功能项
这里以之前做过的一个功能为例,即有个客户想在4.4系统上加一个自动开关机选项在设置里,那么这里只介绍如何加入一个空的功能界面:
首先设置的整个架构已有,参照其他子项的构成,自然是在settings_headers.xml中加入自定义功能header,以自动开关机为例:
<!-- Schedule Power -->
+ <header
+ android:id="@+id/schedule_power_settings"
+ android:fragment="com.android.settings.schedulePower.SchedulePowerSettings"
+ android:icon="@drawable/ic_settings_display"
+ android:title="@string/schedule_power_settings_title" />```
这里一些string,icon资源就不具体位置了,这里的header就会被解析进入到Settings.java里的逻辑。
为了统一,也仿照其他header,在Settings.java的SETTINGS_FOR_RESTRICTED数组里加入:
- R.id.schedule_power_settings,
继而只缺对应的fragment,同样仿照其他header对应的文件,如果不知道怎么写,甚至可以复制其代码,改下名字,删掉所有实现与私有方法,就得到了一个干净的仿照fragment。
我这里即创建SchedulePowerSettings.java:
public class SchedulePowerSettings extends PreferenceFragment{
“`
然后编写布局文件,UI,逻辑实现,和写一个普通fragment并无区别,完成以上步骤,便可以在设置应用里嵌入自己的自动开关机像功能。