基于简单工厂思想的SharePreference组件设计

在app的开发中,让用户设置自己的偏好,能给用户很友好的体验。在android系统中,google很贴心的提供了SharePreference组件,方便开发者存储app的数据。SharePreference提供的API,对简单的基本类型数据,以键值对(key-value)的方式进行的存储,使用极其简单。具体使用教程可以留意google的官方文档,这篇blog主要是介绍使用简单工厂模式开发基于SharePreference的用户偏好设置界面。


先介绍基本的组件,SharePreference数据的可视化的组件PreferenceActivity,以及交互响应接口OnPreferenceChangeListener, OnPreferenceClickListener。


我们可以利用PreferenceActivity的可视化设计器,设计基本的UI控件,并设置其对应的信息,具体如何去设置请看官自己找资料了。

 


基于简单工厂思想的SharePreference组件设计_第1张图片

  

下面是我的一个例子:

 

<?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强大的反射机制来进行设计!


简单工厂的原理如下:

 

基于简单工厂思想的SharePreference组件设计_第2张图片

 

我们的思路如下:

 

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 {

	@Override
	public 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();
	}

	@Override
	public 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 实现类的实例,再去调用对应的方法

 

 

        @Override
	public 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;
	}

	@Override
	public 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中,并于布局文件中引用即可,这样子实现响应的代码就与控制的代码分离了,结构更清晰,维护更简单,当然也符合对修改封闭,对扩展开放的原则!

 

 

你可能感兴趣的:(Preference)