最近,做一個項目,使用 Preference 非常頻繁,之前就很想寫一些關於 android 資料存放區的文章,這次機會來了。
先從小出著手吧,CheckBoxPreference 小巧可愛,使用簡單。但是使用的時候,還是需要注意一些問題。下面開始討論吧,歡迎拍磚!
app 運行之後,效果(很單調):
項目結構圖:
這裡關鍵是在 res/ 目錄下面建立 xml 檔案夾,然後建立我們需要的 Preference 布局(demo.xml)。
demo.xml 檔案:
<?xml version="1.0" encoding="utf-8"?><PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="Demo for checkboxpreference" > <CheckBoxPreference android:key="key_cbp" android:title="checkbox" android:summary="this is a demo" android:summaryOn="open me" android:summaryOff="close me" android:persistent="false"/></PreferenceScreen>
代碼部分,是一個繼承自 PreferenceActivity 的類:
package mark.zhang;import android.os.Bundle;import android.preference.CheckBoxPreference;import android.preference.Preference;import android.preference.PreferenceActivity;import android.preference.Preference.OnPreferenceChangeListener;import android.preference.Preference.OnPreferenceClickListener;import android.util.Log;public class DemoActivity extends PreferenceActivity implements OnPreferenceClickListener, OnPreferenceChangeListener { private static final String KEY = "key_cbp"; private CheckBoxPreference cbp = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.demo); cbp = (CheckBoxPreference) findPreference(KEY); cbp.setOnPreferenceClickListener(this); cbp.setOnPreferenceChangeListener(this); } @Override public boolean onPreferenceClick(Preference preference) { Log.d("mark", "onPreferenceClick is invoked !"); return false; } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { Log.d("mark", "onPreferenceChange is invoked !"); return false; }}
在這個類中,實現兩個介面,介面方法會在點擊該 CheckBoxPreference 時回調。
似乎,上面的內容沒有什麼很難的地方,的確!但是運行 App 之後,你會發現 CheckBox 不可以選擇?!
在 API 的文檔關於 onPreferenceChange 方法中有這麼一句:
True to update the state of the Preference with the new value.
可以看出,在這個方法的結尾返回 true 就可以了,呵呵!這點要注意,細節決定效率嘛!
ok,再看第二個問題,我們知道 Preference 可以自動儲存狀態值,儲存是以 xml 檔案形式儲存的,儲存路徑在 /data/data/packageName/shared_prefs/×××.xml,但是你到這個路徑去找找絕對找不到?!
原因在於我們所寫的 demo.xml 檔案,在這個檔案中,有這麼一句:
android:persistent="false"
這就表示不會儲存 CheckBoxPreference 這個值(是一個 boolean 值,選擇上就是 true,否則就是false)了。
如果,你設定這個屬性為 true,android 就會為你自動儲存,看:
但是為什麼會自動儲存?為什麼檔案名稱就是 mark.zhang_preference 呢?當然是代碼決定的,看源碼,就會一目瞭然了!
當我們點擊 CheckBoxPreference 的時候,會觸發事件:
protected void onClick() { super.onClick(); boolean newValue = !isChecked(); // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change // not sending mSendAccessibilityEventViewClickedType = true; if (!callChangeListener(newValue)) { return; } setChecked(newValue); }
那麽,
setChecked(newValue)
方法就可以調用,
persistBoolean(checked);
方法,該方法是 Preference 類的方法,然後調用 PreferenceManager 相關的方法擷取 SharedPreference 來儲存檔案。
最後,看一個小問題,就是如何動態擷取 CheckBoxPreerence 的值呢?很簡單,實現 OnSharedPreferenceChangeListener 介面,看代碼:
package mark.zhang;import android.content.SharedPreferences;import android.content.SharedPreferences.OnSharedPreferenceChangeListener;import android.os.Bundle;import android.preference.CheckBoxPreference;import android.preference.Preference;import android.preference.PreferenceActivity;import android.preference.Preference.OnPreferenceChangeListener;import android.preference.Preference.OnPreferenceClickListener;import android.util.Log;public class DemoActivity extends PreferenceActivity implements OnPreferenceClickListener, OnPreferenceChangeListener, OnSharedPreferenceChangeListener { private static final String KEY = "key_cbp"; private CheckBoxPreference cbp = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.demo); cbp = (CheckBoxPreference) findPreference(KEY); cbp.setOnPreferenceClickListener(this); // 註冊OnPreferenceChangeListene // cbp.setOnPreferenceChangeListener(this); // 註冊 SharedPreferenceChangeListener // 以便回調 onSharedPreferenceChangeListener getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); } @Override public boolean onPreferenceClick(Preference preference) { Log.d("mark", "onPreferenceClick is invoked !"); if (cbp.isChecked()) { // false Log.d("mark", "cbp.isChecked() = " + cbp.isChecked()); } // return true; return false; } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference.getKey().equals(KEY)) { Log.d("mark", "onPreferenceChange is invoked !" + newValue); // true } // 不可以選擇CheckBox // return false; // 可以選擇CheckBox return true; } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(KEY)) { boolean value = sharedPreferences.getBoolean(key, false); Log.d("mark", "onSharedPreferenceChanged is invoked !" + " and the value = " + value); } } @Override protected void onDestroy() { // 登出 SharedPreferenceChangeListener getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( this); super.onDestroy(); }}
運行之後,可以看到如下列印資訊:
D/mark ( 8077): onSharedPreferenceChanged is invoked ! and the value = falseD/mark ( 8077): onPreferenceClick is invoked !D/mark ( 8077): onSharedPreferenceChanged is invoked ! and the value = trueD/mark ( 8077): onPreferenceClick is invoked !D/mark ( 8077): cbp.isChecked() = true
提醒一點,如果不註冊onSharedPreferenceChangListener:
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
是不會回調 onSharedPreferenceChanged 方法的。