代码改变世界

Android之Preference

2012-08-20 19:32  ...平..淡...  阅读(1332)  评论(0编辑  收藏  举报

首先介绍下什么是 PerferenceActivity,看下面的截图:

image            image

    Android系统截图                   MusicPlayer Setting截图

Android系统本身大量用到了PreferenceActivity来对系统进行信息配置和管理。那么它是怎么保存数据的呢,如何创建PrefenceActivity的呢,更关键是怎样触发相应事件的呢?

 

第一个问题:PreferenceActivity是如何保存的数据。

Android系统有四种基本的数据保存方法:
  1.SharedPreference   2.文件   3.SQLite   4.ContentProvider

Preference,其实是通过SharedPreferneces以键值对的形式进行保存的,当然,我们也可以通过SharedPreferences来获取PreferenceActivity设置的值。

 

第二个问题:如何创建一个PreferenceActivity。 其实Eclipse提供了相应的创建工具,和创建Layout是基本相同的。步骤如下:

创建Android项目,并添加一个Android xml文件。注意,这次选择的不是Layout,而是Preference,而且注意Folder路径是 res/xml

             image

添加完成之后,在res/xml/下打开添加的preference.xml文件。可以看到Android也为我们提供两种编辑模式,可视化的结构设计及xml源码设计。推荐使用structure进行创建。如图所示:

       image

下面我们看看PrefeneceActivity都提供了哪几种元素可供使用。点击Add按钮,在打开的新窗口中可以看到以下几项:

              image

下面,我们对比Preference和View下的各个子控件,对他们的家庭元素在宏观上有更好的把握性。

单一控件:

Preference 控件家庭          View控件家庭      控件含义

Preference                  TextView          文本框

CheckPreference             CheckBox          单选框

EditTextPreference          EditText          输入文本框 

ListPreference              ListView          列表框

RingtonePreference          ——                铃声

其实在Android源码系统中还有很多的”未完工”的Preference, 没有为它们提供PI接口,例如SeekBarPreference,

有兴趣的同学可以参考源码,具体路径为:frameworks/base/core/java/preference。

组合控件:

PreferenceCategory : 类似于LinearLayout、RelativeLayout,用于组合一组Preference,使布局更具备层次感。

PreferenceScreen   : 所有Preference元素的根节点

 

接下来,对每个Preference的独有XML Attributes和方法进行一下总结,使大家能更好地理解。

CheckBoxPreference:CheckBox选择项,对应的值的ture或flase。如图:

            image

EditTextPreference:输入编辑框,值为String类型,会弹出对话框供输入。

            image

ListPreference: 列表选择,弹出对话框供选择。

             image

Preference:只进行文本显示,需要与其他进行组合使用。

           image

PreferenceCategory:用于分组。效果如下:

          image

PreferenceScreen:PreferenceActivity的根元素,必须为它。

 

RingtonePreference:系统玲声选择。

         image

OK,Preference的基本控件和PreferenceActivity介绍完毕,下一节对Preference的各个控件的属性方法等做一个介绍。

---------------------------------------------------------------------------------------------------------------

首先了解Preference的属性及方法

Preference元素的通用XML Attributes说明:    

android:key :每个Preference控件独一无二的”ID”,唯一表示此Preference。          

android:defaultValue : 默认值。 例如,CheckPreference的默认值可为”true”,默认为选中状态;EditTextPreference的默认值可为”110” 。

android:enabled      : 表示该Preference是否可用状态。     
android:title        : 每个Preference在PreferenceScreen布局上显示的标题——大标题
android:summary      : 每个Preference在PreferenceScreen布局上显示的标题——小标题(可以没有)
android:persistent   : 表示Preference元素所对应的值是否写入sharedPreferen文件中,如果是true,则表示写入;否则,则表示不写入该Preference元素的值。
android:dependency   : 表示一个Preference(用A表示)的可用状态依赖另外一个Preference(用B表示)。B可用,则A可用;B不可用,则A不可用。

android:disableDependentsState:  与android:dependency相反。B可用,则A不可用;B不可用,则A可用。

 常用的方法则包括:

 getKey()            setKey()

 getSummary()        setSummary()

 getText()           setText()

简易效果图

 

下面通过分析MusicPlayer Setting来了解各个Preference控件的使用。

image

第一部分为“我的位置”:包括“使用无线网线”和“使用GPS”两个部分,而且都是CheckBox,根据上节学习,应该包括一个PreferenceCategory和两个CheckBoxPreference。

Xml代码:

<PreferenceCategory android:title="我的位置" android:key="set_local">
    <CheckBoxPreference android:key="apply_wifi"
        android:summary="使用无线网络在应用程序(例如Google地图)中查看位置" 
                android:title="使用无线网络" android:defaultValue="true">
    </CheckBoxPreference>
    <CheckBoxPreference android:key="apply_gps"
        android:summary="定位到街道级别(需要消耗更多的电量以及天气允许)" 
                android:title="使用GPS">
    </CheckBoxPreference>
</PreferenceCategory>      
PreferenceCategory属性分析:

android:title:显示的标题 android:key :唯一标识(至少在同一程序中是唯一),SharedPreferences也将通过此Key值进行数据保存,也可以通过key值获取保存的信息 (以下相同)。
CheckBoxPreference属性分析:

android:Key :唯一标识. android:title :显示标题(大字体显示) android:summary :副标题(小字体显示) android:defaultValue:默认值(当然,此处只能是true或false了)

 

Preference.xml的第二部分为"无线和网络设置",此部分包括的内容比较多,也稍微复杂,一步一步来分析。xml代码如下:

<PreferenceCategory android:title="无线和网络设置">
<CheckBoxPreference
    
android:key="apply_fly"
    
android:summary="禁用所有无线连接"
     android:title
="飞行模式"> </CheckBoxPreference>
<CheckBoxPreference
    
android:key="apply_internet"
    
android:summary="禁用通过USB共享Internet连接"     android:title="Internet共享"> </CheckBoxPreference>
<CheckBoxPreference
    
android:key="apply_wifi"
    
android:summary="打开Wi-Fi"
     android:title
="Wi-Fi"> </CheckBoxPreference>
<Preference
     android:summary="设置和管理无线接入点"
     android:title
="Wi-Fi设置"
    
android:dependency="apply_wifi"
     android:key
="wifi_setting"> </Preference>
<CheckBoxPreference
    
android:key="apply_bluetooth"
    
android:summary="启用蓝牙"
     android:title
="蓝牙"> </CheckBoxPreference>
<Preference
     android:summary="管理连接、设备设备名称和可检测性"
    
android:title="蓝牙设置"
     android:dependency
="apply_bluetooth"
    
android:key="bluetooth_setting"> </Preference>
<EditTextPreference
    
android:key="number_edit"
    
android:title="输入电话号码"> </EditTextPreference>
<ListPreference
    
android:title="部门设置"
     android:entries
="@array/department" android:entryValues="@array/department_value" android:dialogTitle="选择部门" android:key="depart_value"> </ListPreference>
<RingtonePreference
    
android:ringtoneType="all"
     android:title
="玲聲" android:showDefault="true"
     android:key
="ring_key"     android:showSilent="true"> </RingtonePreference>
</PreferenceCategory>

第二部分中前三个都为CheckBoxPreference,不心多说,从<Preference android:key="bluetooth_setting"/>开始。

Preference 属性分析:

android :Key:唯一标识. android:title :显示标题(大字体显示) android:summary :副标题(小字体显示) android:dependency:附属,即标识此元素附属于某一个元素(通常为CheckBoxPreference),dependency值为所附属元素的key。上面代码中的Preference元素附属于key等于"apply_bluetooth"的CheckPreference元素,当CheckPreference值为true时,Preference则为可用,否则为不可用。
EditTextPreperence 属性和方法分析:

属性:

android:Key :唯一标识. android:title:显示标题(大字体显示)

方法:
getEditText()  返回的是我们在该控件中输入的文本框值
getText()     返回的是我们之前sharedPreferen文件保存的值
ListPreference 属性和方法分析:

属性:

android:Key :唯一标识. android:title :显示标题(大字体显示) android:dialogTitle:弹出对话框的标题 android:entries :列表中显示的值。为一个数组,通读通过资源文件进行设置。 android:entryValues:列表中实际保存的值,也entries对应。为一个数组,通读通过资源文件进行设置。以下代码显示的是arrays.xml文件中内容:
<resources>
    <string-array name="department">
        <item>综合部</item>
        <item>行政部</item>
        <item>外贸部</item>
    </string-array>
    <string-array name="department_value">
        <item>001</item>
        <item>002</item>
        <item>003</item>
    </string-array>
</resources>
方法:
CharSequence[] getEntries(): 返回的是控件显示文本的一个”key”数组,对应于属性android:entries CharSequence[] getEntryValues():返回的一个”value”数组,对应于属性android: entryValues CharSequence getEntry(): 返回当前选择文本 String getValue() :返回当前选中文本选中的value 。
RingtonePreference属性分析:

属性: android:ringtoneType:响铃的铃声类型,主要有:ringtone(音乐)、notification(通知)、alarm(闹铃)、all(所有可用声 音类型)。 android:showDefault :默认铃声,可以使用系统(布尔值
---true,false)的或者自定义的铃声 android:showSilent :指定铃声是否为静音。指定铃声包括系统默认铃声或者自定义的铃声

OK,Preference.xml内容已经分析完毕,属性都大致相同。

 --------------------------------------------------------------------------

那么如何把Preference.xml中内容展现出来呢?

PreferenceActivity是专门用于显示preference的,所以只要创建一个继承自PreferenceActivity类即可。代码如下,在onCreate方法中通过addPreferencesFromResource()实现:

1 public class Setting extends PreferenceActivity {
2     @Override
3     public void onCreate(Bundle savedInstanceState) {
4         super.onCreate(savedInstanceState);
5         // 所的的值将会自动保存到SharePreferences
6         addPreferencesFromResource(R.xml.preference);
7     }
8 }

接下来就是运行程序,显示劳动成果。至此,工作已经完成大半,所有的值都会保存到SharedPreferences中,我们也可以读取到保存的结果。

重点:分析Preference事件

那么,如何响应PreferenceActivity的操作。其实只要重写PreferenceActivity的 onPreferenceTreeClick方法就可以了,通过参数preference来判断是对那一个元素进行的,并根据需要进行操作。

★在PreferenceActivity方法中,一个比较重要的监听点击事件方法为:

public booleanonPreferenceTreeClick (PreferenceScreen preferenceScreen, Preference preference)

说明    : 当Preference控件被点击时,触发该方法。
参数说明: preference 点击的对象。

返回值  : true  代表点击事件已成功捕捉,无须执行默认动作或者返回上层调用链。 例如,不跳转至默认Intent。

          false 代表执行默认动作并且返回上层调用链。例如,跳转至默认Intent。

在我们继承PreferenceActivity的Activity可以重写该方法,来完成我们对Preference事件的捕捉。

  

相信通过前面的介绍,你一定知道了如何使用了Preference家族并且对其触发方法。下面我们抛出另外两枚炸弹——Preference相关的两个重要监听接口。

★ Preference.OnPreferenceChangeListener     该监听器的一个重要方法如下:

boolean onPreferenceChange(Preference preference,Object objValue)

说明  :当Preference的元素值发送改变时,触发该事件。

返回值:true  代表将新值写入sharedPreference文件中。

       false 则不将新值写入sharedPreference文件
★ Preference.OnPreferenceClickListener      该监听器的一个重要方法如下:

public booleanonPreferenceClick(Preference preference)

说明:当点击控件时触发发生,可以做相应操作。

                            

那么当一个Preference控件实现这两个接口时,当被点击或者值发生改变时,触发方法是如何执行的呢?事实上,

 它的触发规则如下:

1 先调用onPreferenceClick()方法,
(1) 如果该方法返回true,则不再调用onPreferenceTreeClick方法 ; (2) 如果onPreferenceClick方法返回false,则继续调用onPreferenceTreeClick方法。
2 onPreferenceChange的方法独立与其他两种方法的运行。也就是说,它总是会运行。

到此关于PreferenceActvity和Preference的分析基本结束。

--------------------------------------------------------------------

获取配置信息

为了方便的获取配置信息,我们可以在PreferenceActivity里添加一些pulbic 方法来公开配置信息的访问。
View Code
 1 /**
 2      * 获取是否播放声音
 3      */
 4     public static boolean playSounds(Context context) {
 5         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SOUNDS, SOUNDS_DEFAULT);
 6     }
 7     
 8     /**
 9      * 设置播放的声音
10      * @param context 上下文
11      * @param value 是否播放
12      */
13     public static void setPlaySounds(Context context, boolean value) {
14         PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(SOUNDS, value).commit();
15     }
16     
17     /**
18      * 获取警告时间
19      * @param context 上下文
20      * @return 警告时间秒
21      */
22     public static int getWarningTime(Context context) {
23         String value=PreferenceManager.getDefaultSharedPreferences(context).getString(WARNING_TIME,Integer.toString(WARNING_TIME_DEFAULT));
24         try
25         {
26             return Integer.parseInt(value);
27         }
28         catch(NumberFormatException e)
29         {
30              setWarningTime(context, WARNING_TIME_DEFAULT);
31              return WARNING_TIME_DEFAULT;
32         }
33         
34     }
35     
36     /**
37      * 设置警告时间
38      * @param context 上下文
39      * @param warningTime 警告时间
40      */
41     public static void setWarningTime(Context context, int warningTime) {
42      PreferenceManager.getDefaultSharedPreferences(context).edit().putString(WARNING_TIME, Integer.toString(warningTime)).commit();
43      
44     }
45     
46     /**
47      * 参加人数无限制
48      * @param context
49      * @return
50      */
51     public static boolean allowUnlimitedParticipants(Context context) {
52         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(UNLIMITED_PARTICIPANTS, UNLIMITED_PARTICIPANTS_DEFAULT);
53     }
54 
55     public static void setAllowUnlimitedParticipants(Context context, boolean value) {
56         PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(UNLIMITED_PARTICIPANTS, value).commit();
57     }
58 
59     /**
60      * 允许编辑会议时间
61      * @param context
62      * @return
63      */
64     public static boolean allowVariableMeetingLength(Context context) {
65         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(VARIABLE_MEETING_LENGTH, VARIABLE_MEETING_LENGTH_DEFAULT);
66     }
67 
68     public static void setAllowVariableMeetingLength(Context context, boolean value) {
69         PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(VARIABLE_MEETING_LENGTH, value).commit();
70     }

 

getDefaultSharedPreferences(Context )用来获取preferences。以后的操作就和普通的Sharedpreferences一样了,如果需要修改某项配置的信息,记得最后需要 commit()。当其他地方需要使用配置时,可以直接调用 setting.getXXX() 方法来获取配置信息。



Preferencescreen的应用:它不仅可以作为设置界面显示,而且还能够启动activity,下面主要是对启动activity的介绍

1. Preferencescreen中启动activity

例如wireless_setting.xml中有如下片段

<PreferenceScreen 
     xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:settings="http://schemas.android.com/apk/res/com.seedshope.android"> <PreferenceScreen android:key="wifi_settings" android:title="@string/wifi_settings" android:summary="@string/wifi_settings_summary" > <intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.settings" android:targetClass="com.android.settings.wifi.WifiSettings" /> </PreferenceScreen> </PreferenceScreen>

其中com.android.settings是工程的包名,com.android.settings.wifi.WifiSettings是要启动的类。

一般情况下这样就ok了,点击对应的Preference就能启动对应的activity,然而仅仅这样,并不一定能够启动对应的activity,因为它与回调函数onPreferenceTreeClick有关,有时候我们会重写这个方法如:

 1 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
 2        if ( (preference == mAirplaneModePreference) && (Boolean.parseBoolean(SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) ) {
 4             // In ECM mode launch ECM app dialog
 5             startActivityForResult( new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), REQUEST_CODE_EXIT_ECM);
 8             return true;
 9        }
10        else {
11             // Let the intents be launched by the Preference manager
12             return false;
13        }
14     }

其中WirelessSetting.java中有如上函数,当返回值为true时,你怎么点击Preference都不会跳转到activity的,只有等到它的返回值为false的时候,才可以正常跳转,因为点击某一个Preference的真正实现在Preference.java的performClick函数中如下:

 1   void performClick(PreferenceScreen preferenceScreen) {
 2        if (!isEnabled()) {
 3            return;
 4        }
 5        onClick();
 6     if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
 7          return;
 8     }
 9     PreferenceManager preferenceManager = getPreferenceManager();
10     if (preferenceManager != null) {
11         PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager.getOnPreferenceTreeClickListener();
12            if (preferenceScreen != null && listener != null && listener.onPreferenceTreeClick(preferenceScreen, this)) {
13                return;
14            }
15     }
16     if (mIntent != null) {
17            Context context = getContext();
18            context.startActivity(mIntent);
19        }
20   }

当点击某个Preference时的调用流程是

AdapterView.performItemClick--->PreferenceScreen.onItemClick--->Preference.performClick--->PreferenceActivity.onPreferenceTreeClick

当onPreferenceTreeClick返回true的时候就直接return了,没有走下面启动activity的地方了,因此要使一个Preference能够正常跳转到另外一个activity有两个条件,

1.xml里面是否设置正确

2.调用该xml的java类是否在onPreferenceTreeClick这个函数需要返回false

转自:

http://blog.csdn.net/qinjuning/article/details/6710003

http://www.cnblogs.com/joey-home/archive/2012/02/03/2336778.html