PREFERENCE的一个比较全面的总结
2014-03-10 23:31
441 查看
我们看到Android系统本身就大量用到了PreferenceActivity来对系统进行信息配置和管理,比如android系统自带的settings应用源码就是用PrederenceActivity来实现的.
android系统源码的自带的apk应用的源码都在\packages\apps改目录下,比如我们说的Settings,Email,Browser等
preference下的View是有限的,只有下面几个:
CheckBoxPreference:CheckBox选择项,对应的值的ture或flase
EditTextPreference:输入编辑框,值为String类型,会弹出对话框供输入。
ListPreference: 列表选择,弹出对话框供选择。
Preference:只进行文本显示,需要与其他进行组合使用。
PreferenceCategory:用于分组
PreferenceScreen:PreferenceActivity的根元素,必须为它
RingtonePreference:系统玲声选择
具体的各个View介绍可以参考下面的博文:Android之PreferenceActivity基本介绍
然后还有就是各个View元素的属性介绍也参考Android之PreferenceActivity基本介绍博文,介绍的也比较详细
Preferences本质是通过SharedPreferences基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。
其存储位置在/data/data/<包名>/shared_prefs目录下。
SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。
实现SharedPreferences存储的步骤如下:
一、根据Context获取SharedPreferences对象
二、利用edit()方法获取Editor对象。
三、通过Editor对象存储key-value键值对数据。
四、通过commit()方法提交数据。
SharedPreferences可以保存Boolean、Int、Float、Long、String等类型。
一般是SharedPreferences.Editor的putXXX()方法保存,并commit()方法提交;或
者是remove(),方法移除,clear()方法清空,当然操作完毕后需要commit()方法提交
我们知道,获取就是直接用SharedPreferences调用getXXX,都是写入保存必须用Editor对象才可以.
获得SharedPreferences,如果需要进行保存等修改操作,首先得通过其edit()方法获得SharedPreferences.Editor,然后就可以通过putInt、putString等方法以键值对(key-value)的方式保存数据,或者remove移除某个键(key),及调用clear方法删除所有内容。最后需要调用commit方法是使修改生效。
最后还需要注意的是,我们的Activity需要继承PreferenceActivity类和OnPreferenceChangeListener,OnPreferenceClickListener接口,并且覆写
public boolean onPreferenceChange(Preference preference, ObjectnewValue)和
public boolean onPreferenceClick(Preference preference)
在这2个函数中我们必须自己来实现界面上的更新和通过SharedPreference相关接口实现数据的保存。
onPreferenceChange和onPreferenceClick区别也很明显,选择选中元素单击肯定会调用onPreferenceClick,如果修改了元素的属性就会调用onPreferenceChange()
比如ListPreference来说,我们肯定先单击,那么肯定会调用onPreferenceClick,这时还会弹出选择条目,如果我们直接退出不选择,肯定就不会调用onPreferenceChange(),
如果我们操作了选择条目,就算是和原来的属性一样,也会调用onPreferenceChange()的
如果需要自定义PreferenceActivity的界面风格或者设计自己的AndroidPreference,可以参考下面博文:Preference使用小结和PreferenceActivity类介绍
源码可以参考DV6300-T的tv_setting源码,以及自己写的关于PreferenceActivity测试源码PreferenceActivtiyTest,在E:\JAVA\Android\DV6300-T测试代码,代码中有详细解释
参考博文:
Android数据的四种存储方式之一:SharedPreferences
android数据存储之Shared
Preferences
Shared Preferences使用
Android的设置界面及Preference使用
Android之PreferenceActivity基本介绍
Preference使用小结
PreferenceActivity类介绍
ListActivity的onListItemClick不响应的解决办法
转自:http://www.cnblogs.com/franksunny/archive/2011/10/21/2219890.html
在Symbian实现类似如下配置参数的设置界面
需要复杂的自定义列表来实现,在android中由于SDK封装和提供了一套基于Preference的类,使用Preference通过编辑xml配置文件,只要很少的代码就可以实现了,而且Preference本身已经实现了参数保存,不需要我们再考虑将参数保存文件,下面让我们来认识下Preference。
Preference需要通过Activity才能显示出来,SDK封装了一个抽象类PreferenceActivity专门提供我们派生自己需要的Activity。和Activtiy需要layout布局一样,这里的PreferenceActivity实例化的时候也是需要XML布局文件,该布局文件可以通过“File”“New”“Android XML File”菜单弹出如下对话框来生成
在资源类型中选择Preference,在root element中保持默认即选择PreferenceScreen,否则在Activity中绑定该资源时,将报“java.lang.RuntimeException: Unable to start activity……”的类似错误。
其实在Preference XML资源文件中,元素标签(element)类型主要有有两类:一类是管理布局的有PreferenceScreen和PreferenceCategory;另一类是具体的设置元素,有CheckBoxPreference、ListPreference、EditTextPreference和RingtonePreference等。假设我们要实现如下图所示的效果
首先,我们需要生成一个Preference资源文件,命名为preferencescategory.xml,具体内容如下
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Settings">
<PreferenceCategory
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Emotions"
android:summary="settings about emotions">
<CheckBoxPreference
android:title="Love me?"
android:summaryOn="Yes,I love you!"
android:summaryOff="No,I am sorry."
android:defaultValue="true" android:key="@string/category_loveme_key">
</CheckBoxPreference>
<CheckBoxPreference
android:title="Hate me?"
android:summaryOn="Yes,I hate you!"
android:summaryOff="No,you are a good person."
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
<PreferenceCategory
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Relations"
android:summary="settings about relations">
<CheckBoxPreference
android:title="Family?"
android:summaryOn="Yes,we are family!"
android:summaryOff="No,I am sorry."
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:title="Friends?"
android:summaryOn="Yes,we are friends!"
android:summaryOff="No,I am sorry."
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
</PreferenceScreen>
其次,我们从PreferenceActivity派生一个PreferenceCategoryActivity类,具体代码如下
public class PreferenceCategoryActivity extends PreferenceActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferencescategory);
}
}
再次,将这个Activity添加到AndroidManifest.xml中,假设该Activity被设置为起始Activity,那么程序一运行就呈现上述界面。
其实通过上述的列子,我们可以看到当修改参数配置后,退出程序,再重新进入程序,出现的配置参数是更改以后,而并非我们初始化设置的。这个参数配置保存功能是怎么实现的呢?答案是SDK提供了一个SharedPreferences来实现上述功能的。所以很多参考书中将SharedPreferences与文件和SQLite一起被放置在Android数据存储章节,用以获取和修改持久化存储的数据。需要注意的是这种方式主要用来存储比较简单的一些数据,而且是标准的Boolean、Int、Float、Long、String等类型
那么这些参数具体保存在哪里呢?通过模拟器我们可以发现,在data/data/包名/shared_prefs/下面存在着若干xml文件,具体如下图所示
Android就是靠这些文件来实现参数配置的保存的。我们如何通过SharedPreferences来实现对这些文件访问和修改呢。
SDK提供了三个获取SharedPreferences的函数,分别是
public SharedPreferences getPreferences (int mode)
public SharedPreferences getSharedPreferences (String name, int mode)
public static SharedPreferences getDefaultSharedPreferences (Context context)
前两个是非静态类,需要通过具体的Acitvity对象或者在Activity对象内调用,最后一个可以通过静态方法调用。
第一个getPreferences函数,操作的是属于Activity自身的Preference参数配置文件,文件名是Actvity的类名,一个Activity只能有一个该类配置文件,比如上述图示中的PreferenceDemoActivity.xml就是属于PreferenceDemoActivity参数配置文件。
第二个getSharedPreferences函数,操作的是属于整个应用程序的参数配置文件,一个应用程序可以包含有多个该类配置文件,文件名是函数第一参数的name。在上述图示中就是形如loveme.xml这类文件。
第三个getDefaultSharedPreferences函数,操作的也是属于整个应用程序的参数配置文件,不过该类文件一个应用程序只有一个,文件名为“包名_preferences”,这类配置文件就是用来保存Preference布局文件中元素定义的参数配置的。
有了SharedPreferences之后,我们就可以通过其提供的如下接口函数来获取储存的配置参数。
boolean getBoolean(String key, boolean defValue);
float getFloat(String key, float defValue);
long getLong(String key, long defValue);
int getInt(String key, int defValue);
String getString(String key, String defValue);
Map<String, ?> getAll();
也可以通过SharedPreferences.Editor来修改配置参数,具体见如下代码
SharedPreferences vPreferences = getSharedPreferences(checkbox_key, Activity.MODE_PRIVATE);
boolean vLoveme = vPreferences.getBoolean(checkbox_key, true);
SharedPreferences.Editor editor = vPreferences.edit();
editor.putBoolean(checkbox_key, false);
editor.commit();
当PreferenceActivity中的内容改变时,Android系统会自动进行保存和持久化维护,我们只需要在要用的设置界面中需要数据的地方进行读取就可以了。同时Android还提供了OnPreferenceClickListener和OnPreferenceChangeListener两个与Preference相关的监听接口,当PreferenceActivity中的某一个Preference进行了点击或者改变的操作时,都会回调接口中的函数,这样可以第一个时间向其他Activity等通知系统设置进行了改变。下面提供一份拦截监听的代码
public class PreferenceDemoActivity extends PreferenceActivity implements OnPreferenceChangeListener,
OnPreferenceClickListener
{
/** Called when the activity is first created. */
CheckBoxPreference vCheckBox;
String checkbox_key;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
addPreferencesFromResource(R.xml.pref);
checkbox_key = getResources().getString(R.string.love_me);
vCheckBox = (CheckBoxPreference)findPreference(checkbox_key);
//注册修改函数
vCheckBox.setOnPreferenceChangeListener(this);
vCheckBox.setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
// TODO Auto-generated method stub
//判断是哪个Preference改变了
if(preference.getKey().equals(checkbox_key))
{
}
else
{
//如果返回false表示不允许被改变
return false;
}
//返回true表示允许改变
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
// TODO Auto-generated method stub
//判断是哪个Preference被点击了
if(preference.getKey().equals(checkbox_key))
{
}
else
{
return false;
}
return true;
}
}
通过调用发现当点击CheckBox时,先调用onPreferenceChange,之后再调用onPreferenceClick,所以个人感觉,很多情况如果只对参数感兴趣,可以不用拦截点击监听。
以上是对Preference基础使用的小结,系统提供的Preference毕竟太少,为此我们通常需要用到自定义的Preference。下面就介绍自定义Preference的使用。
系统提供的Preference样式还是少了点,为了呈现丰富的UI,很多时候我们需要自定义Preference,自定义Preference有两种实现方法:第一种实现方法仅通过资源xml的修改来实现自定义Preference的效果;第二种方法是通过派生类的方法来实现自定义Preference的效果。下面分别阐述如下:
修改资源的方法
系统默认的Preference风格是黑底白字的样式,有时候我们需要改变下字体颜色或者字体类型,抑或我们想要修改下CheckBox的图标不是系统自带的勾子,而是自定义的图标,假设入下图的效果
那么该如何实现呢?
上述样式的实现,借助于每个Preference的android:layout和android:widgetLayout属性,给其重新布局,重新布局的时候需要参考frameworks\base\core\res\res下面的原有布局,否则在不清楚其资源格式的情况下进行修改往往达不到效果,通过这里的尝试,我发现假设自定义的资源在在加载过程中出错或者类似解析不符,那么系统会默认加载缺省的资源。
下面针对上图自定义preference中的第一个CheckBoxPreference的实现,给出参考了frameworks\base\core\res\res\layout文件夹下面的preference.xml布局文件,增添文本颜色等参数而重构的custom_preferece_layout.xml文件的内容(其中的红色字体就是原有基础上新增加的属性)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
<RelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="@+android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:textColor="#00FF00" />
<TextView android:id="@+android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#FF0000"
android:textStyle="bold|italic"
android:maxLines="4" />
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@+android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="vertical" />
</LinearLayout>
如上修改很少,但是效果达到了。至于将默认的打勾图标,切换成自己的图标,也是依样画葫芦,找到源码frameworks\base\core\res\res\layout文件夹下的preference_widget_checkbox.xml,然后重构为custom_check_widget.xml,内容如下(其中红色字体时原有布局基础上新增加的,关于其中button的实现就不贴出来,参考demo程序吧)
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="@drawable/selfcheckbtn"
android:layout_marginRight="4dip"
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false" />
在Preference的资源文件中定义这个CheckBoxPreference的代码就如下所示
<CheckBoxPreference
android:defaultValue="true"
android:summaryOff="禁止自动搜索"
android:summaryOn="允许自动搜素"
android:key="auto_search_enable_key"
android:title="自动搜素"
android:disableDependentsState="false"
android:layout="@layout/custom_preferece_layout"
android:widgetLayout="@layout/custom_check_widget">
</CheckBoxPreference>
通过修改资源布局来实现自定义Preference的样式,假如对Android的资源深入了解后实现起来就能随心所欲而且手到擒来了,具体就不展开了,详见示例程序。
派生类的方法
上面方法仅仅从资源的角度去修改Preference的样式,其实SDK提供了Preference基类,我们除了可以使用系统提供的CheckBoxPreference、ListPreference、EditTextPreference和RingtonePreference外,还可以自己定义我们需要的Preference。遵循从资源到代码的逻辑,使用系统自带的CheckBoxPreference我们可以方便地再xml文件中使用“CheckBoxPreference”元素来定义,那么派生类Preference的资源元素该用什么标签呢?
自定义的Preference的资源元素标签就是自定义Preference的包名加类名,下面我们实现一个自定义的带图标的设置项,由于类名为ImageOptionPreference,而包名为netease.frank.demo.selfImagePreference,所以资源中设置的元素名为netease.frank.demo.selfImagePreference.ImageOptionPreference,具体的xml文件内容如下
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<netease.frank.demo.selfImagePreference.ImageOptionPreference
android:title="发送短信"
android:summary="点击按钮将切入短信发送界面"
android:key="game_pic"
android:widgetLayout="@layout/preference_widget_image"/>
</PreferenceScreen>
在xml中用到一个widgetLayout,其xml文件内容如下所示
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="@+id/pref_current_img"
android:src="@drawable/icon"
android:layout_marginRight="4dip"
android:layout_height="54dip"
android:padding="2dip"
android:layout_width="54dip"
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false" />
</LinearLayout>
类定义代码,我们需要提供一个构造函数和一个点击时操作函数(在这里我们让其发送一条短信)既可,简单代码如下
public class ImageOptionPreference extends Preference
{
public ImageOptionPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@Override
protected void onClick()
{
// super.onClick();
Uri uri = Uri.parse("smsto:0800000123");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "The SMS text");
this.getContext().startActivity(it);
}
}
这样我们就可以如同SDK提供的CheckboxPreference一样在PreferenceActivity中使用我们自己的定义的ImageOptionPreference。
上面这个自定义ImageOptionPreference派生自Preference,很多情况下我们不需要从这么底层的类派生,而从DialogPreference派生我们的需求类就可以了,下面演示一个时间设置的空间。我们将这个时间设置类封装为类名为TimePreference,而包名为 netease.frank.demo.selfImagePreference,我们将其放置在上述这个ImageOptionPreference的下面一项,在selfimagepre.xml的定义就如下所示
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<netease.frank.demo.selfImagePreference.ImageOptionPreference
android:title="发送短信"
android:summary="点击按钮将切入短信发送界面"
android:key="game_pic"
android:widgetLayout="@layout/preference_widget_image"/>
<netease.frank.demo.selfImagePreference.TimePreference
android:key="time_test"
android:summary="打开修改时间"
android:title="时间设置"
android:defaultValue="1000000"/>
</PreferenceScreen>
上面的android:defaultValue是当参数没有设置情况下的缺省值。自定义DialogPreferenc自然我们还需要一个满足我们需求的对话框布局文件,这里的时间设置对话框布局time_preference.xml文件,具体代码如下所示
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TimePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/timePicker_preference"
android:layout_centerHorizontal="true">
</TimePicker>
</RelativeLayout>
而相应的类的实现代码,如下所示:
public class TimePreference extends DialogPreference
{
TimePicker mPicker = null;
long mValue ;
public TimePreference(Context context, AttributeSet attrs)
{
super(context, attrs);
//设置对话框需要加载的布局文件
setDialogLayoutResource(R.layout.time_preference);
}
//弹出对话框的时候进行初始化
@Override
protected void onBindDialogView(View view)
{
super.onBindDialogView(view);
mPicker = (TimePicker)view.findViewById(R.id.timePicker_preference);
if(mPicker != null)
{
mPicker.setIs24HourView(true);
long value = mValue;
Date d = new Date(value);
mPicker.setCurrentHour(d.getHours());
mPicker.setCurrentMinute(d.getMinutes());
}
}
//关闭对话框的时候保存
@Override
protected void onDialogClosed(boolean positiveResult)
{
super.onDialogClosed(positiveResult);
if(positiveResult)
{
Date d = new Date(0, 0, 0, mPicker.getCurrentHour(), mPicker.getCurrentMinute(), 0);
long value = d.getTime();
mValue = value;
if(callChangeListener(value))
{
SharedPreferences.Editor vEditor = getEditor();
vEditor.putLong(getKey(), value);//(checkbox_key, false);
vEditor.commit();
}
}
}
//获取缺省的配置参数
@Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
mValue = Long.parseLong(a.getString(index));
return mValue;
}
//获取sharepreference中的配置参数,该函数在配置文件存在时才会被调用
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
long value;
if(restorePersistedValue)
value = getPersistedLong(1000000);
else
{
value = Long.parseLong(defaultValue.toString());
}
setDefaultValue(value);
mValue = value;
}
}
在上述的TimePreference类的代码过程中,需要注意以下几点:
onGetDefaultValue的调用,先于TimePreference的构造函数之前运行,从而确保缺省参数的获得;
查看一下DialogPreference的onClick函数的实现,我们就知道在这里我们为什么不用重载OnClick,而是重载了onBindDialogView函数就可以了,同理如果我们想重载OnClick函数也是可行的;
虽然获取参数,系统帮我们实现了,但是保存参数的操作,还是需要我们自己来提供,所以在对话框关闭的onDialogClosed,我们对设置的时间值进行了保存;
关于“包名_preferences.xml”参数配置文件,程序一开始运行的时候是不存在的,所以第一次运行程序时,程序不会调用onSetInitialValue,只有当程序执行过一次保存后,参数配置文件才被创建,从而才会被执行调用。
关于Preference就介绍到这里,非常感谢老华的指点。
另,本小结提供三个demo程序,分别如下
PreferencesDemo 用于演示和说明系统原有的Preference的特性和使用
PreferenceDemo 用于演示和说明Preference的三种SharedPreferences保存类型
selfImagePreferenceDemo 用于演示和说明自定义Preference的两种方法
demo和原文下载
android系统源码的自带的apk应用的源码都在\packages\apps改目录下,比如我们说的Settings,Email,Browser等
preference下的View是有限的,只有下面几个:
CheckBoxPreference:CheckBox选择项,对应的值的ture或flase
EditTextPreference:输入编辑框,值为String类型,会弹出对话框供输入。
ListPreference: 列表选择,弹出对话框供选择。
Preference:只进行文本显示,需要与其他进行组合使用。
PreferenceCategory:用于分组
PreferenceScreen:PreferenceActivity的根元素,必须为它
RingtonePreference:系统玲声选择
具体的各个View介绍可以参考下面的博文:Android之PreferenceActivity基本介绍
然后还有就是各个View元素的属性介绍也参考Android之PreferenceActivity基本介绍博文,介绍的也比较详细
Preferences本质是通过SharedPreferences基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。
其存储位置在/data/data/<包名>/shared_prefs目录下。
SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。
实现SharedPreferences存储的步骤如下:
一、根据Context获取SharedPreferences对象
二、利用edit()方法获取Editor对象。
三、通过Editor对象存储key-value键值对数据。
四、通过commit()方法提交数据。
SharedPreferences可以保存Boolean、Int、Float、Long、String等类型。
一般是SharedPreferences.Editor的putXXX()方法保存,并commit()方法提交;或
者是remove(),方法移除,clear()方法清空,当然操作完毕后需要commit()方法提交
我们知道,获取就是直接用SharedPreferences调用getXXX,都是写入保存必须用Editor对象才可以.
获得SharedPreferences,如果需要进行保存等修改操作,首先得通过其edit()方法获得SharedPreferences.Editor,然后就可以通过putInt、putString等方法以键值对(key-value)的方式保存数据,或者remove移除某个键(key),及调用clear方法删除所有内容。最后需要调用commit方法是使修改生效。
最后还需要注意的是,我们的Activity需要继承PreferenceActivity类和OnPreferenceChangeListener,OnPreferenceClickListener接口,并且覆写
public boolean onPreferenceChange(Preference preference, ObjectnewValue)和
public boolean onPreferenceClick(Preference preference)
在这2个函数中我们必须自己来实现界面上的更新和通过SharedPreference相关接口实现数据的保存。
onPreferenceChange和onPreferenceClick区别也很明显,选择选中元素单击肯定会调用onPreferenceClick,如果修改了元素的属性就会调用onPreferenceChange()
比如ListPreference来说,我们肯定先单击,那么肯定会调用onPreferenceClick,这时还会弹出选择条目,如果我们直接退出不选择,肯定就不会调用onPreferenceChange(),
如果我们操作了选择条目,就算是和原来的属性一样,也会调用onPreferenceChange()的
如果需要自定义PreferenceActivity的界面风格或者设计自己的AndroidPreference,可以参考下面博文:Preference使用小结和PreferenceActivity类介绍
源码可以参考DV6300-T的tv_setting源码,以及自己写的关于PreferenceActivity测试源码PreferenceActivtiyTest,在E:\JAVA\Android\DV6300-T测试代码,代码中有详细解释
参考博文:
Android数据的四种存储方式之一:SharedPreferences
android数据存储之Shared
Preferences
Shared Preferences使用
Android的设置界面及Preference使用
Android之PreferenceActivity基本介绍
Preference使用小结
PreferenceActivity类介绍
ListActivity的onListItemClick不响应的解决办法
转自:http://www.cnblogs.com/franksunny/archive/2011/10/21/2219890.html
在Symbian实现类似如下配置参数的设置界面
需要复杂的自定义列表来实现,在android中由于SDK封装和提供了一套基于Preference的类,使用Preference通过编辑xml配置文件,只要很少的代码就可以实现了,而且Preference本身已经实现了参数保存,不需要我们再考虑将参数保存文件,下面让我们来认识下Preference。
PREFERENCEACTIVITY布局文件
Preference需要通过Activity才能显示出来,SDK封装了一个抽象类PreferenceActivity专门提供我们派生自己需要的Activity。和Activtiy需要layout布局一样,这里的PreferenceActivity实例化的时候也是需要XML布局文件,该布局文件可以通过“File”“New”“Android XML File”菜单弹出如下对话框来生成 在资源类型中选择Preference,在root element中保持默认即选择PreferenceScreen,否则在Activity中绑定该资源时,将报“java.lang.RuntimeException: Unable to start activity……”的类似错误。
其实在Preference XML资源文件中,元素标签(element)类型主要有有两类:一类是管理布局的有PreferenceScreen和PreferenceCategory;另一类是具体的设置元素,有CheckBoxPreference、ListPreference、EditTextPreference和RingtonePreference等。假设我们要实现如下图所示的效果
首先,我们需要生成一个Preference资源文件,命名为preferencescategory.xml,具体内容如下
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Settings">
<PreferenceCategory
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Emotions"
android:summary="settings about emotions">
<CheckBoxPreference
android:title="Love me?"
android:summaryOn="Yes,I love you!"
android:summaryOff="No,I am sorry."
android:defaultValue="true" android:key="@string/category_loveme_key">
</CheckBoxPreference>
<CheckBoxPreference
android:title="Hate me?"
android:summaryOn="Yes,I hate you!"
android:summaryOff="No,you are a good person."
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
<PreferenceCategory
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="Relations"
android:summary="settings about relations">
<CheckBoxPreference
android:title="Family?"
android:summaryOn="Yes,we are family!"
android:summaryOff="No,I am sorry."
android:defaultValue="true">
</CheckBoxPreference>
<CheckBoxPreference
android:title="Friends?"
android:summaryOn="Yes,we are friends!"
android:summaryOff="No,I am sorry."
android:defaultValue="false">
</CheckBoxPreference>
</PreferenceCategory>
</PreferenceScreen>
其次,我们从PreferenceActivity派生一个PreferenceCategoryActivity类,具体代码如下
public class PreferenceCategoryActivity extends PreferenceActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferencescategory);
}
}
再次,将这个Activity添加到AndroidManifest.xml中,假设该Activity被设置为起始Activity,那么程序一运行就呈现上述界面。
PREFERENCE配置参数的保存
其实通过上述的列子,我们可以看到当修改参数配置后,退出程序,再重新进入程序,出现的配置参数是更改以后,而并非我们初始化设置的。这个参数配置保存功能是怎么实现的呢?答案是SDK提供了一个SharedPreferences来实现上述功能的。所以很多参考书中将SharedPreferences与文件和SQLite一起被放置在Android数据存储章节,用以获取和修改持久化存储的数据。需要注意的是这种方式主要用来存储比较简单的一些数据,而且是标准的Boolean、Int、Float、Long、String等类型 那么这些参数具体保存在哪里呢?通过模拟器我们可以发现,在data/data/包名/shared_prefs/下面存在着若干xml文件,具体如下图所示
Android就是靠这些文件来实现参数配置的保存的。我们如何通过SharedPreferences来实现对这些文件访问和修改呢。
SDK提供了三个获取SharedPreferences的函数,分别是
public SharedPreferences getPreferences (int mode)
public SharedPreferences getSharedPreferences (String name, int mode)
public static SharedPreferences getDefaultSharedPreferences (Context context)
前两个是非静态类,需要通过具体的Acitvity对象或者在Activity对象内调用,最后一个可以通过静态方法调用。
第一个getPreferences函数,操作的是属于Activity自身的Preference参数配置文件,文件名是Actvity的类名,一个Activity只能有一个该类配置文件,比如上述图示中的PreferenceDemoActivity.xml就是属于PreferenceDemoActivity参数配置文件。
第二个getSharedPreferences函数,操作的是属于整个应用程序的参数配置文件,一个应用程序可以包含有多个该类配置文件,文件名是函数第一参数的name。在上述图示中就是形如loveme.xml这类文件。
第三个getDefaultSharedPreferences函数,操作的也是属于整个应用程序的参数配置文件,不过该类文件一个应用程序只有一个,文件名为“包名_preferences”,这类配置文件就是用来保存Preference布局文件中元素定义的参数配置的。
有了SharedPreferences之后,我们就可以通过其提供的如下接口函数来获取储存的配置参数。
boolean getBoolean(String key, boolean defValue);
float getFloat(String key, float defValue);
long getLong(String key, long defValue);
int getInt(String key, int defValue);
String getString(String key, String defValue);
Map<String, ?> getAll();
也可以通过SharedPreferences.Editor来修改配置参数,具体见如下代码
SharedPreferences vPreferences = getSharedPreferences(checkbox_key, Activity.MODE_PRIVATE);
boolean vLoveme = vPreferences.getBoolean(checkbox_key, true);
SharedPreferences.Editor editor = vPreferences.edit();
editor.putBoolean(checkbox_key, false);
editor.commit();
拦截监听接口
当PreferenceActivity中的内容改变时,Android系统会自动进行保存和持久化维护,我们只需要在要用的设置界面中需要数据的地方进行读取就可以了。同时Android还提供了OnPreferenceClickListener和OnPreferenceChangeListener两个与Preference相关的监听接口,当PreferenceActivity中的某一个Preference进行了点击或者改变的操作时,都会回调接口中的函数,这样可以第一个时间向其他Activity等通知系统设置进行了改变。下面提供一份拦截监听的代码public class PreferenceDemoActivity extends PreferenceActivity implements OnPreferenceChangeListener,
OnPreferenceClickListener
{
/** Called when the activity is first created. */
CheckBoxPreference vCheckBox;
String checkbox_key;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
addPreferencesFromResource(R.xml.pref);
checkbox_key = getResources().getString(R.string.love_me);
vCheckBox = (CheckBoxPreference)findPreference(checkbox_key);
//注册修改函数
vCheckBox.setOnPreferenceChangeListener(this);
vCheckBox.setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue)
{
// TODO Auto-generated method stub
//判断是哪个Preference改变了
if(preference.getKey().equals(checkbox_key))
{
}
else
{
//如果返回false表示不允许被改变
return false;
}
//返回true表示允许改变
return true;
}
@Override
public boolean onPreferenceClick(Preference preference) {
// TODO Auto-generated method stub
//判断是哪个Preference被点击了
if(preference.getKey().equals(checkbox_key))
{
}
else
{
return false;
}
return true;
}
}
通过调用发现当点击CheckBox时,先调用onPreferenceChange,之后再调用onPreferenceClick,所以个人感觉,很多情况如果只对参数感兴趣,可以不用拦截点击监听。
以上是对Preference基础使用的小结,系统提供的Preference毕竟太少,为此我们通常需要用到自定义的Preference。下面就介绍自定义Preference的使用。
自定义PREFERENCE
系统提供的Preference样式还是少了点,为了呈现丰富的UI,很多时候我们需要自定义Preference,自定义Preference有两种实现方法:第一种实现方法仅通过资源xml的修改来实现自定义Preference的效果;第二种方法是通过派生类的方法来实现自定义Preference的效果。下面分别阐述如下: 修改资源的方法
系统默认的Preference风格是黑底白字的样式,有时候我们需要改变下字体颜色或者字体类型,抑或我们想要修改下CheckBox的图标不是系统自带的勾子,而是自定义的图标,假设入下图的效果
那么该如何实现呢?
上述样式的实现,借助于每个Preference的android:layout和android:widgetLayout属性,给其重新布局,重新布局的时候需要参考frameworks\base\core\res\res下面的原有布局,否则在不清楚其资源格式的情况下进行修改往往达不到效果,通过这里的尝试,我发现假设自定义的资源在在加载过程中出错或者类似解析不符,那么系统会默认加载缺省的资源。
下面针对上图自定义preference中的第一个CheckBoxPreference的实现,给出参考了frameworks\base\core\res\res\layout文件夹下面的preference.xml布局文件,增添文本颜色等参数而重构的custom_preferece_layout.xml文件的内容(其中的红色字体就是原有基础上新增加的属性)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize">
<RelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dip"
android:layout_marginRight="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="@+android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:textColor="#00FF00" />
<TextView android:id="@+android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#FF0000"
android:textStyle="bold|italic"
android:maxLines="4" />
</RelativeLayout>
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@+android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:orientation="vertical" />
</LinearLayout>
如上修改很少,但是效果达到了。至于将默认的打勾图标,切换成自己的图标,也是依样画葫芦,找到源码frameworks\base\core\res\res\layout文件夹下的preference_widget_checkbox.xml,然后重构为custom_check_widget.xml,内容如下(其中红色字体时原有布局基础上新增加的,关于其中button的实现就不贴出来,参考demo程序吧)
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="@drawable/selfcheckbtn"
android:layout_marginRight="4dip"
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false" />
在Preference的资源文件中定义这个CheckBoxPreference的代码就如下所示
<CheckBoxPreference
android:defaultValue="true"
android:summaryOff="禁止自动搜索"
android:summaryOn="允许自动搜素"
android:key="auto_search_enable_key"
android:title="自动搜素"
android:disableDependentsState="false"
android:layout="@layout/custom_preferece_layout"
android:widgetLayout="@layout/custom_check_widget">
</CheckBoxPreference>
通过修改资源布局来实现自定义Preference的样式,假如对Android的资源深入了解后实现起来就能随心所欲而且手到擒来了,具体就不展开了,详见示例程序。
派生类的方法
上面方法仅仅从资源的角度去修改Preference的样式,其实SDK提供了Preference基类,我们除了可以使用系统提供的CheckBoxPreference、ListPreference、EditTextPreference和RingtonePreference外,还可以自己定义我们需要的Preference。遵循从资源到代码的逻辑,使用系统自带的CheckBoxPreference我们可以方便地再xml文件中使用“CheckBoxPreference”元素来定义,那么派生类Preference的资源元素该用什么标签呢?
自定义的Preference的资源元素标签就是自定义Preference的包名加类名,下面我们实现一个自定义的带图标的设置项,由于类名为ImageOptionPreference,而包名为netease.frank.demo.selfImagePreference,所以资源中设置的元素名为netease.frank.demo.selfImagePreference.ImageOptionPreference,具体的xml文件内容如下
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<netease.frank.demo.selfImagePreference.ImageOptionPreference
android:title="发送短信"
android:summary="点击按钮将切入短信发送界面"
android:key="game_pic"
android:widgetLayout="@layout/preference_widget_image"/>
</PreferenceScreen>
在xml中用到一个widgetLayout,其xml文件内容如下所示
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="@+id/pref_current_img"
android:src="@drawable/icon"
android:layout_marginRight="4dip"
android:layout_height="54dip"
android:padding="2dip"
android:layout_width="54dip"
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false" />
</LinearLayout>
类定义代码,我们需要提供一个构造函数和一个点击时操作函数(在这里我们让其发送一条短信)既可,简单代码如下
public class ImageOptionPreference extends Preference
{
public ImageOptionPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@Override
protected void onClick()
{
// super.onClick();
Uri uri = Uri.parse("smsto:0800000123");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "The SMS text");
this.getContext().startActivity(it);
}
}
这样我们就可以如同SDK提供的CheckboxPreference一样在PreferenceActivity中使用我们自己的定义的ImageOptionPreference。
上面这个自定义ImageOptionPreference派生自Preference,很多情况下我们不需要从这么底层的类派生,而从DialogPreference派生我们的需求类就可以了,下面演示一个时间设置的空间。我们将这个时间设置类封装为类名为TimePreference,而包名为 netease.frank.demo.selfImagePreference,我们将其放置在上述这个ImageOptionPreference的下面一项,在selfimagepre.xml的定义就如下所示
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<netease.frank.demo.selfImagePreference.ImageOptionPreference
android:title="发送短信"
android:summary="点击按钮将切入短信发送界面"
android:key="game_pic"
android:widgetLayout="@layout/preference_widget_image"/>
<netease.frank.demo.selfImagePreference.TimePreference
android:key="time_test"
android:summary="打开修改时间"
android:title="时间设置"
android:defaultValue="1000000"/>
</PreferenceScreen>
上面的android:defaultValue是当参数没有设置情况下的缺省值。自定义DialogPreferenc自然我们还需要一个满足我们需求的对话框布局文件,这里的时间设置对话框布局time_preference.xml文件,具体代码如下所示
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TimePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/timePicker_preference"
android:layout_centerHorizontal="true">
</TimePicker>
</RelativeLayout>
而相应的类的实现代码,如下所示:
public class TimePreference extends DialogPreference
{
TimePicker mPicker = null;
long mValue ;
public TimePreference(Context context, AttributeSet attrs)
{
super(context, attrs);
//设置对话框需要加载的布局文件
setDialogLayoutResource(R.layout.time_preference);
}
//弹出对话框的时候进行初始化
@Override
protected void onBindDialogView(View view)
{
super.onBindDialogView(view);
mPicker = (TimePicker)view.findViewById(R.id.timePicker_preference);
if(mPicker != null)
{
mPicker.setIs24HourView(true);
long value = mValue;
Date d = new Date(value);
mPicker.setCurrentHour(d.getHours());
mPicker.setCurrentMinute(d.getMinutes());
}
}
//关闭对话框的时候保存
@Override
protected void onDialogClosed(boolean positiveResult)
{
super.onDialogClosed(positiveResult);
if(positiveResult)
{
Date d = new Date(0, 0, 0, mPicker.getCurrentHour(), mPicker.getCurrentMinute(), 0);
long value = d.getTime();
mValue = value;
if(callChangeListener(value))
{
SharedPreferences.Editor vEditor = getEditor();
vEditor.putLong(getKey(), value);//(checkbox_key, false);
vEditor.commit();
}
}
}
//获取缺省的配置参数
@Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
mValue = Long.parseLong(a.getString(index));
return mValue;
}
//获取sharepreference中的配置参数,该函数在配置文件存在时才会被调用
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
long value;
if(restorePersistedValue)
value = getPersistedLong(1000000);
else
{
value = Long.parseLong(defaultValue.toString());
}
setDefaultValue(value);
mValue = value;
}
}
在上述的TimePreference类的代码过程中,需要注意以下几点:
onGetDefaultValue的调用,先于TimePreference的构造函数之前运行,从而确保缺省参数的获得;
查看一下DialogPreference的onClick函数的实现,我们就知道在这里我们为什么不用重载OnClick,而是重载了onBindDialogView函数就可以了,同理如果我们想重载OnClick函数也是可行的;
虽然获取参数,系统帮我们实现了,但是保存参数的操作,还是需要我们自己来提供,所以在对话框关闭的onDialogClosed,我们对设置的时间值进行了保存;
关于“包名_preferences.xml”参数配置文件,程序一开始运行的时候是不存在的,所以第一次运行程序时,程序不会调用onSetInitialValue,只有当程序执行过一次保存后,参数配置文件才被创建,从而才会被执行调用。
关于Preference就介绍到这里,非常感谢老华的指点。
另,本小结提供三个demo程序,分别如下
PreferencesDemo 用于演示和说明系统原有的Preference的特性和使用
PreferenceDemo 用于演示和说明Preference的三种SharedPreferences保存类型
selfImagePreferenceDemo 用于演示和说明自定义Preference的两种方法
demo和原文下载
相关文章推荐
- 工作流Activiti的学习总结- 整合比较复杂的一个流程
- 小雷郑重承诺:在2017年之前,对大学毕业4年以来的所有努力和探索,做一个全面客观的总结,技术研究、工作创业、投资理财、朋友感情等
- 2014项目总结:一个比较成功的项目总结
- 一个比较全面游戏开发技术网站
- 找到一份比较全面的freemarker语法总结
- 小雷郑重承诺:在2017年之前,对大学毕业4年以来的所有努力和探索,做一个全面客观的总结,技术研究、工作创业、投资理财、朋友感情等
- 自己总结的一个移动菜单方法,比较简陋哦
- 找到一份比较全面的freemarker语法总结
- 一个对前端模板技术的全面总结
- 绘制不规则位图方法总结,多种实现方法,全面测试比较
- JVM调优总结(这个总结得比较全面)
- 找到一份比较全面的freemarker语法总结
- 一个学习python 比较全面的 blog
- HTMLParser(一个比较流行的html代码解析、处理开源项目)学习,总结
- 一个比较全面的ColdFusion FAQ
- 一个关于live2d资料比较全面的网站
- javascript全面总结(可能别人看的有点乱,不过多看几遍就习惯了,每个*是一个知识点集合)
- 比较全面的一个自定义导航栏
- 比较全面的freemarker语法总结
- 发现一个总结的非常全面的android开源框架