Setting模块深入分析

时间:2022-12-12 15:49:50

      今天公司的老项目进行大整改,其中我负责的模块之一就是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家族的对比:

View家族和Preference家族的相同控件对比
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的,我们来看下其继承关系图:

       Setting模块深入分析

          正如上图所示,空色箭头表示是直接继承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,我们去看看该类: