今天公司的老项目进行大整改,其中我负责的模块之一就是Setting模块,因为之前并没有对完整的做过Setting模块,所以今天把这个模块完成后就开始对Setting这一块详细的了解下并对源码进行分析~~,如果完全没有基础的可以移步此博客:http://blog.csdn.net/qinjuning/article/details/6710003/,本文的部分内容就是基于该博客来完善的~
我们首先模仿手机系统的设置界面来感受下其用法,现在res/xml目录下创建一个叫setting的xml文件(名字随意起),该文件就是设置界面,其内容如下:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="系统设置" >
<!-- Wifi -->
<SwitchPreference
android:icon="@drawable/ic_signal_wifi_0_bar_white_36dp"
android:key="setting_wifi"
android:title="Wi-fi" />
<!-- 蓝牙 -->
<SwitchPreference
android:icon="@drawable/ic_bluetooth_disabled_white_36dp"
android:key="setting_bluetooth"
android:title="蓝牙" />
<Preference
android:icon="@drawable/ic_brightness_medium_white_36dp"
android:key="setting_light"
android:title="亮度" />
<Preference
android:icon="@drawable/ic_wallpaper_white_36dp"
android:key="setting_wallpaper"
android:title="壁纸" />
<Preference
android:icon="@drawable/ic_font_download_white_36dp"
android:key="setting_fontsize"
android:title="字体大小" />
<Preference
android:icon="@drawable/ic_screen_lock_landscape_white_36dp"
android:key="setting_lockscreen"
android:title="屏幕锁定" />
</PreferenceScreen>
上面的xml代码就是一个没有加任何效果的Setting界面,甚至每一个条目都没有icon。界面出来了,现在的问题就是如何加载,和使用Activity一样,在Android为Setting模块专门提供了一个PreferenceActivity类,我们的设置界面就是继承此类来完成,在此类的onCreate()方法中调用addPreferencesFromResource()方法来完成加载,如下是加载setting文件的类:
public class SettingActivity extends PreferenceActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.setting);
}
}
就是上面几行代码,一个简单的设置界面就完成了,下面我们先来了解下Setting的家族有哪些控件。
Preference家族体系
实际上,Preference家族和View的家族继承结构类似,View是所有view的祖先,Preference则是所有preference的祖先,下面就是Preference家族和View家族的对比:
Preference控件名称 | View控件名称 | 控件意义 |
Preference | TextView | 显示内容,有单击事件 |
EditTextPreference | EditText | 编辑框 |
ListPreference | ListView | 列表显示 |
CheckBoxPreference | CheckBox | 选择框 |
SwitchPreference | Switch | 开关控件 |
除此之外,Preference还有一个RingtonePreference声音控件和一个MultiSelectListPreference多选择控件。需要注意的是在Preference家族中对于控件来说只有DialogPreference、RingtonePreference和TwoStatePreference是直接继承自Preference的,而DialogPreference和TwoStatePreference并不能直接使用;EditTextPreference、ListPreference和MultiSelectListPreference是继承DialogPreference的,CheckBoxPreference和SwitchPreference是继承自TwoStatePreference的,我们来看下其继承关系图:
正如上图所示,空色箭头表示是直接继承Preference的类,蓝色表示的是间接继承Preference的类。正如我们所看到的那样,,在Preference家族中还有一个PreferenceGroup类,这个类其实和View系统的容器类是一样的设计思想,也是用于包含其控件类作为一组的,PreferenceGroup有两个子类PreferenceCategory和PreferenceScreen两个类,其实我们从上面的xml代码中已经看出来这两个类的用法了,PreferenceScreen是作为界面的根布局来使用的,PreferenceCategory是作为分组来使用的。
Preference控件的属性
Preference类中的属性,即所有Preference控件所共有的属性
icon:图标,默认的只在内容左侧,自定义除外。
key:该Preference的唯一标识,相当于View的id属性。
title:该Preference的标题(通常一个默认的Preference是由一个标题+一个内容组成)。
summary:该Preference的内容。
order:该Preference所在PreferenceCategory的顺序,其值是>=0的整数,0表示在第一个位置。
fragment:当单击该PreferenceFragment(目前我还没有发现此属性可用)。
layout:为该Preference自定义一个界面,如果使用了自定义的界面那么其他属性(除了key属性)就都不会起到作用了,比如title、summary、icon等。
widgetlayout:为该Preference定义右侧的部件,类似SwitchPreference的开关按钮部件,但是左侧的文字部分仍然是系统界面。这个属性和layout属性不同,layout属性是完全将系统的Preference的界面替换为自己的,但是widgetlayout只是让我们去定义自己的右侧部件,比如Preference类是没有类似于SwitchPreference的右侧开关部件的,但是我们可以在Preference控件中使用widgetlayout属性来实现自己的“开关”。
enable:设置该Preference是否可用,如果设为false,那么它就会变暗,甚至没有点击事件。
selectable:设置该Preference是否可选择,对CheckBoxPreference有效。
presistent:如果设置为true,此Preference的值就会保存到SharedPreference文件中,如果为false,则不会保存。
dependancy:设置该Preference依赖项,该值是其所依赖的Preference的key值,比如一个EditTextPreference的dependancy属性设置为CheckBoxPreference的key值,那么只有当CheckBoxPreference为选中状态下该EditTextPreference才会enable,否则会处于disable状态。
defaultValue:为该Preference设置默认的值,尤其是EditTextPreference控件有效。
Preference子类所特有的属性
DialogPreference的属性
dialogTitle:弹出对话框时该对话框的标题。
dialogMessage:弹出对话框时该对话框的内容,和View的Dialog一样。
dialogIcon:弹出对话框的图标。
positiveButtonText:弹出对话框的“确定”按钮文本,系统默认显示的是“确定”字样,我们可以设置成任意文本文字。
positiveButtonText:弹出对话框的“取消”按钮文本,系统默认显示的是“取消”字样,我们可以设置成任意文本文字。
dialogLayout:自定义弹出对话框布局。
注意,由于我们不能直接使用DialogPreference控件,我们只能使用其三个子类EditTextPreference、ListPreference和MultiSelectListPreference控件,这三个控件拥有以上所有的属性,而且没有拓展其他属性。
CheckBoxPreference的属性
CheckBoxPreference相较于Preference类拓展了四个属性。
summaryOn:当选中时,summary显示的文本文字。
summaryOff:当没有选中时,summary显示的文本文字。
disableDependentsState:设置他的依赖值是否可用。
Preference的事件交互
Preference的单击事件
只有当Preference控件处于enable状态下才有单击事件,如下注册单击事件:
<span style="font-size:14px;"> //注册单击事件
mMorePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
//当单击Wi-Fi时销毁Setting界面
finish();
//如果返回true表示单击事件被处理,否则就没有被处理
return true;
}
});
</span>
Preference的单击事件过程分析
在Preference类中有一个OnPreferenceClickListener接口,里面只有一个onPreferenceClick(Preference preference)方法,并且还有一个setOnPrefeClickListener()方法来注册单击监听,那么onPreferenceClick()方法是什么时候被执行的呢?当然是我们单击的时候,那么单击的时候是怎么被出发调用的呢?我们从下往上分析。首先onPreferenceClick()是客户端重写的一个系统回调的方法,该方法是在Preference的 void performClick(PreferenceScreen preferenceScreen)
方法中被调用的,该方法如下:
/**
* Called when a click should be performed.
*
* @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
* listener should be called in the proper order (between other
* processing). May be null.
*/
void performClick(PreferenceScreen preferenceScreen) {
//是否可用,不可用直接return
if (!isEnabled()) {
return;
}
//此方法在Preference中是一个空的,并且是一个Protected类型的,很明显是在我们自定义Preference的时候可以重写的
onClick();
//执行onPreferenceClick()方法
if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
return;
}
PreferenceManager preferenceManager = getPreferenceManager();
if (preferenceManager != null) {
PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
.getOnPreferenceTreeClickListener();
if (preferenceScreen != null && listener != null
&& listener.onPreferenceTreeClick(preferenceScreen, this)) {
return;
}
}
if (mIntent != null) {
Context context = getContext();
context.startActivity(mIntent);
}
}
PerformClick()方法是在PreferenceScreen类中的onItemClick()方法中执行的。它是继承ListActivity的。
Preference的数据存储
Preference数据都存储在手机上的一个xml文件中,其默认的名字是包名+“_preferences”构成,源码如下:
//获取SharedePreferences对象
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
getDefaultSharedPreferencesMode());
}
//生成一个默认的文件名
private static String getDefaultSharedPreferencesName(Context context) {
return context.getPackageName() + "_preferences";
}
然后获取某属性比如EditTextPreference控件的值是就有SharedPreference对象根据各Preference的key值get即可。
Preference界面相关
1、PreferenceActivity的默认界面如何修改?
我们可以看到,在我们的SettingActivity中并没有使用类似的setContentView()来设置View,仅仅是调用addPreferencesFromResource(R.xml.setting)来完成,似乎这个setting.xml就是界面,但是实际上Preference家族和View是完全不沾边的,那么既然PreferenceActivity是一个Activity,我们去看看该类: