基于简单工厂思想的SharePreference组件设计
在app的开发中,让用户设置自己的偏好,能给用户很友好的体验。在android系统中,google很贴心的提供了SharePreference组件,方便开发者存储app的数据。SharePreference提供的API,对简单的基本类型数据,以键值对(key-value)的方式进行的存储,使用极其简单。具体使用教程可以留意google的官方文档,这篇blog主要是介绍使用简单工厂模式开发基于SharePreference的用户偏好设置界面。
先介绍基本的组件,SharePreference数据的可视化的组件PreferenceActivity,以及交互响应接口OnPreferenceChangeListener, OnPreferenceClickListener。
我们可以利用PreferenceActivity的可视化设计器,设计基本的UI控件,并设置其对应的信息,具体如何去设置请看官自己找资料了。
?
下面是我的一个例子:
?
<?xml version="1.0" encoding="utf-8"?><PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/system_settings" > <PreferenceCategory android:title="@string/email_category" > <EditTextPreference android:dialogTitle="@string/change_email_dialog_title" android:key="@string/change_email_account_key" android:negativeButtonText="@string/cancel" android:positiveButtonText="@string/config" android:summary="@string/change_email_account_summary" android:title="@string/change_email_account_title" android:selectable="true"/> </PreferenceCategory> <PreferenceCategory android:title="@string/ring_category" > <CheckBoxPreference android:dependency="@string/receive_sms_command_key" android:enabled="true" android:key="@string/ring_key" android:summaryOff="@string/ring_summary_off" android:summaryOn="@string/ring_summary_on" android:title="@string/ring_title" android:selectable="true" android:defaultValue="true"/> </PreferenceCategory></PreferenceScreen>??
之后可以在PreferenceActivity通过方法addPreferencesFromResource加载该xml文件即可显示你设计好的偏好设置界面。
之后就是监听用户的交互动作,一般就是Preference的OnClick事件还有OnChange事件,并在对应的监听器中加以处理响应,完成设置流程。
这里值得注意的是,即使你不去监听并响应一般事件,Preference也会自动保存用户更改操作后的状态,你的app可以通过具体的Preference引用获取到其当前的value。当然你也可以通过在OnChange事件响应中,把具体的状态值键值对形式保存到SharePreference中,以便app能更方便地访问到用户设置的value。
可是这样子的话,问题就来了。我们得在监听器中为每一个Preference发生的事件进行监听并响应!假如Preference只有几个还比较容易维护,但是app的迭代周期是很短的,几次迭代后,需求可能会变化巨大,十几个Preference的事件响应代码就难以修改和维护了。而且这也违反了对修改封闭,对扩展开放的原则。
然而每个Preference的行为又是那么的简单,只有onClick和onChange!为了简便各Preference的行为响应,我们可以利用简单工厂模式以及Java强大的反射机制来进行设计!
简单工厂的原理如下:
?

?
我们的思路如下:
?
1、设计一个行为接口IPrefListener?,定义行为doWhenOnClick和doWhenChange:
?
?
public interface IPrefListener {public boolean doWhenChange(Context ctx, Object o);public boolean doWhenOnClick(Preference pref);}?2、通过实现IPrefListener,使具体的Preference具有各自的响应行为。
?
?
public class EmailPrefChangeListener implements IPrefListener {@Overridepublic boolean doWhenChange(Context ctx, Object o) {String address = (String) o;boolean isOk = BaseUtils.isRightEmailAddress(address);if(isOk){updateCache(ctx,address);showTipsForUser(ctx);} else {DialogFactory df = new DialogFactory(ctx);Dialog dialog = df.onCreateDialog(DialogFactory.DIALOG_ID_EMAIL_ERROR);dialog.show();}return isOk;}private void updateCache(Context ctx, String account){SharePrefHelper.getInstance(ctx).writeData(PrefConst.KEY_EMAIL_ACCOUNT, account);}private void showTipsForUser(Context ctx){String tips = ctx.getResources().getString(R.string.toast_email_account_change_succ);Toast.makeText(ctx, tips, Toast.LENGTH_SHORT).show();}@Overridepublic boolean doWhenOnClick(Preference pref) {String defaultValue = SharePrefHelper.getInstance(pref.getContext()).readData(PrefConst.KEY_EMAIL_ACCOUNT, "");EditTextPreference etPref = (EditTextPreference) pref;etPref.getEditText().setText(defaultValue);return true;}}??
?
3、然后利用PreferenceActivity作为工厂,并实现OnPreferenceChangeListener, OnPreferenceClickListener接口响应各Preference的事件。
?
? 初始化布局中的Preference的监听器,并在事件响应方法中,利用Preference的key反射获得具体的IPrefListener 实现类的实例,再去调用对应的方法:
?
?
@Overridepublic boolean onPreferenceChange(Preference preference, Object newValue) {Log.i(TAG, preference.getKey()+"had change, new value:"+newValue);boolean isChangeSucc = true;String key = preference.getKey();try {//利用java的反射机制,new出一个负责当前设置变更响应的处理类实例IPrefListener listener = (IPrefListener) this.getApplicationContext().getClassLoader().loadClass(key).newInstance();isChangeSucc = listener.doWhenChange(SettingActivity.this, newValue);} catch (Exception e) {e.printStackTrace();} return isChangeSucc;}@Overridepublic boolean onPreferenceClick(Preference preference) {Log.i(TAG, preference.getKey()+"had click");boolean isChangeSucc = true;String key = preference.getKey();try {//利用java的反射机制,new出一个负责当前设置变更响应的处理类实例IPrefListener listener = (IPrefListener) this.getApplicationContext().getClassLoader().loadClass(key).newInstance();isChangeSucc = listener.doWhenOnClick(preference);} catch (Exception e) {e.printStackTrace();} return isChangeSucc;}??
上面我们有一个技巧,就是把具体的IPrefListener?实现类的完整类名作为Preference的key,通过这样,实现接口的类就和对应的Preference联系起来了!
以后再有偏好设置要增加进来,只需要在布局文件布局好,然后实现IPrefListener的对应类, 把其完整的类名配置到String.xml中,并于布局文件中引用即可,这样子实现响应的代码就与控制的代码分离了,结构更清晰,维护更简单,当然也符合对修改封闭,对扩展开放的原则!
?
?