導讀
最近做項目無意中看到Perference類,發現Android系統設置就是用這個實現,感覺自己以前寫的設置頁面都白寫了,而且還浪費時間
關於設置頁面的介紹這裏就不詳細介紹了,官方文檔已經寫得很詳細,官方推薦我們使用在XML文件中聲明Preference類的各種子類構建設置頁面,而不是使用View對象構建用戶界面
下面是我的學習總結,希望能輔助到小白學習吧..!(趕時間直接想用的跳到Preference簡單使用)
官方文檔Preference列出來的類結構樹
==補充說明==
- Preference類提供了SharedPreference用於保存或讀取數據.key屬性作爲SharedPreference的鍵.最終保存在data/data/包名/shared_prefs文件下.
- 由於應用的設置UI使用的是Preference對象構建而成,隱藏需要專門的Activity或Fragment子類顯示列表設置.
- 如果應用版本低於Android 3.0(API 10),必需將Activity構建爲PreferenceActivity類的擴展
- 對於Android 3.0 以上的版本,在傳統的Activitiy中使用PreferenceFragment來顯示
在data/data/包名/shared_prefs 導出文件如下
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<!--ListPreference在SharedPreference保存的狀態-->
<string name="key_List">1</string>
<!--MultiSelectListPreference在SharedPreference保存的狀態-->
<set name="key_MultiSelectList">
<string>1</string>
<string>2</string>
</set>
<!--EditTextPreference在SharedPreference保存的狀態-->
<string name="key_EditText">fedhfghhfdghdffghdfgh</string>
<!--CheckBoxPreference在SharedPreference保存的狀態-->
<boolean name="key_CheckBoxPreference" value="true" />
<!--RingtonePreference在SharedPreference保存的狀態-->
<string name="key_RingtonePreference">content://media/internal/audio/media/6</string>
</map>
PreferenceScreen
- PreferenceScreen作爲PreferenceActivity基本佈局的根容器,類似普通佈局的各種Layout
- 可以通過xml方式或者通過createPreferenceScreen(Context)方式來構造PreferenceScreen
- PreferenceScreen可以被PreferenceCategory嵌套,也可以PreferenceScreen嵌套PreferenceScreen,將通過新開屏幕顯示
PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="first_preferencescreen">
<CheckBoxPreference
android:key="wifi enabled"
android:title="WiFi" />
<PreferenceScreen
android:key="second_preferencescreen"
android:title="WiFi settings">
<CheckBoxPreference
android:key="prefer wifi"
android:title="Prefer WiFi" />
... other preferences here ...
</PreferenceScreen>
</PreferenceScreen>
PreferenceCategory
通常作爲二級容器嵌套在PreferenceScreen裏提供分組的作用,類似數據庫SQL中的group by.
EditTextPreference(輸入框Dialog)
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!--EditText getEditText() 獲取顯示在Dialog上的EditText-->
<!--String getText() 獲取對應SharedPreferences保存的值-->
<!--String setText() 設置對應SharedPreferences保存的值-->
<EditTextPreference
android:dialogTitle="ETPreference"
android:key="key_EditText"
android:summary="EditTextPreference summary"
android:title="EditTextPreference"/>
</PreferenceScreen>
ListPreference(單選框列表Dialog)
<!--android:entries 列表的顯示項數據源-->
<!--android:entryValues 列表實際保存至內部的值-->
<!--android:defaultValue 這個屬性不是特有的,但是有一點需要注意:這裏設置的是android:entryValues裏的值-->
<!--android:dialogMessage 如果設置了這個屬性,那麼列表怎麼會被覆蓋掉-->
<!--int findIndexOfValue(String value) 返回獲取ListPreference中的實體內容的下標值-->
<!--CharSequence[] getEntries() 返回當前Preference設置的entries集合(xml配置的字符串數據源)-->
<!--CharSequence getEntry() 返回當前選中的entries值-->
<!--CharSequence[] getEntryValues() 返回當前的entries對應的value集合(xml配置的字符串數據源)-->
<!--CharSequence getSummary() 獲取當前summary-->
<!--String getValue() 獲取key值-->
<ListPreference
android:dialogTitle="List"
android:entries="@array/game"
android:entryValues="@array/game_index"
android:key="key_List"
android:summary="ListPreference summary"
android:title="ListPreference"
/>
MultiSelectListPreference(多選框列表Dialog)
<!--MultiSelectListPreference新增方法 說明-->
<!--int findIndexOfValue(String value) 返回獲取ListPreference中的實體內容的下標值-->
<!--CharSequence[] getEntries() 返回當前Preference 設置的entries集合(xml配置的字符串數據源)-->
<!--CharSequence[] getEntryValues() 返回當前的entries對應的value集合(xml配置的字符串數據源)-->
<!--Set getValues() 返回當前選中的values,SharedPreference的-->
<!--void setEntries(int entriesResId)-->
<!--void setEntries(CharSequence[] entries)-->
<!--void setEntryValues(CharSequence[] entryValues)-->
<!--void setEntryValues(int entryValuesResId)-->
<!--void setValues(Set values)-->
<MultiSelectListPreference
android:dialogTitle="MultiSelectList"
android:entries="@array/game"
android:entryValues="@array/game_index"
android:key="key_MultiSelectList"
android:summary="MultiSelectListPreference summary"
android:title="MultiSelectListPreference"/>
res/values/array.xml定義
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--ListPreference 要顯示的數據-->
<string-array name="game">
<item>Dota</item>
<item>Dota2</item>
<item>NBA2K10</item>
<item>FIFA2K10</item>
</string-array>
<string-array name="game_index">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</resources>
CheckBoxPreference
<!--android:widgetLayout 選中框佈局-->
<!--android:summaryOff uncheck時的summary-->
<!--android:summaryOn check時的summary-->
<CheckBoxPreference
android:icon="@mipmap/ic_launcher"
android:key="key_checkbox_preference"
android:order="10"
android:summary="CheckboxPreference summary"
android:summaryOff="Summary off"
android:summaryOn="On Summary"
android:title="CheckBoxPreference"/>
SwitchPreference
<!--android:summaryOff uncheck時的summary-->
<!--android:summaryOn check時的summary-->
<!--android:switchTextOff 處於off狀態,switch使用的文本-->
<!--android:switchTextOn-->
<SwitchPreference
android:key="key_SwitchPreference"
android:order="1"
android:summary="SwitchPreference summary"
android:summaryOff="Off "
android:summaryOn="On"
android:switchTextOff="關"
android:switchTextOn="開"
android:title="SwitchPreference"
android:widgetLayout="@layout/activity_show"
/>
RingtonePreference
RingtonePreference起作用就是供我們選擇系統鈴聲的
特有屬性 | 說明 |
---|---|
android:ringtoneType | 鈴聲類型:ringtone(1)、notification(2)、all(7)、alarm(4) |
android:showDefault | 布爾值是否顯示默認鈴聲 |
android:showSilent | 布爾值是否顯示靜音 |
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="using_categories_in_root_screen"
android:summary="Using Preference Categories"
android:title="Categories">
<RingtonePreference
android:key="key_prerence"
android:title="RingPreferece Title"
android:summary="RingPreference Summary"
/>
</PreferenceScreen>
自定義Preference(主要是自定義控件方式)
public class NumberPickerPreference extends DialogPreference {
public NumberPickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(R.layout.numberpicker_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
}
...
}
Preference監聽事件的回調接口和方法
一、監聽某個Preference被點擊回調
findPreference("Preference").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Toast.makeText(getActivity(), "Preference 被點擊了", Toast.LENGTH_SHORT).show();
return true;
}
});
二、監聽任意首選項狀態發生改變時立即收到通知
//強引用方式創建監聽器,防止監聽器被垃圾回收掉
//這個回調只有狀態發生改變纔會被調用
SharedPreferences.OnSharedPreferenceChangeListener listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// listener implementation
if (key.equals("key_EditTextPreference")) {
Preference connectionPref = findPreference(key);
// Set summary to be the user-description for the selected value
Preference key_EditTextPreference = findPreference("key_EditTextPreference");
key_EditTextPreference.setSummary(sharedPreferences.getString(key, "") + "");
}
}
};
//生命週期管理,在onResume()和onPause()回調期間分別註冊和註銷
@Override
protected void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(listener);
}
@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(listener);
}
Preference簡單使用
一、在res/xml/目錄下創建爲根節點的xml文件,並按照上述的XXXPreference設置屬性
<PreferenceCategory
android:title="PreferenceCategory"
>
<!--android:widgetLayout 修改Preference裏的子佈局-->
<!--android:layout 修改Preference的佈局-->
<Preference
android:icon="@mipmap/ic_launcher_round"
android:summary="Open otherActivity (Preference)"
android:title="Open otherActivity"
>
</Preference>
</PreferenceCategory>
<!--嵌套PreferenceScreen 會再打開一個子屏幕-->
<!--可以使用extra給intent標籤加參數來傳遞參數實現意圖 會再打開一個子屏-->
<PreferenceScreen
android:summary="Open the Web (Preference)"
android:title="嵌套PreferenceScreen Open the Web "
>
<Preference
android:title="Open the Web (Preference)"
>
<intent
android:action="android.intent.action.VIEW"
android:data="http://www.hao123.com"/>
</Preference>
</PreferenceScreen>
<!--選擇系統鈴聲的-->
<!--android:ringtoneType 鈴聲類型:ringtone、notification、all、alarm-->
<!--android:showDefault 布爾值是否顯示默認鈴聲-->
<!--android:showSilent 布爾值是否顯示靜音-->
<RingtonePreference
android:key="key_RingtonePreference"
android:ringtoneType="all"
android:showDefault="true"
android:showSilent="true"
android:summary="RingPreference (鈴聲設置)"
android:title="RingtonePreference"
/>
<!--android:order 首選項的順序(較低的值在前面)-->
<EditTextPreference
android:key="key_EditTextPreference"
android:order="1"
android:summary="EditTextPreference 輸入框"
android:title="EditTextPreference"/>
<Preference
android:key="key_Preference"
android:summary="please Click me"
android:title="Preference Click Test"
/>
二、初始化PreferenceActivity或PreferenceFragment,按需設置相關監聽器
public class NormalPreferenceFragment extends PreferenceFragment {
private RingtonePreference mKey_ringtonePreference;
private Preference mKey_preference;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//引入Preference XML文件
addPreferencesFromResource(R.xml.normal_preference);
mKey_ringtonePreference = (RingtonePreference) findPreference("key_RingtonePreference");
int ringtoneType = mKey_ringtonePreference.getRingtoneType();
boolean showDefault = mKey_ringtonePreference.getShowDefault();
boolean showSilent = mKey_ringtonePreference.getShowSilent();
mKey_preference = findPreference("key_Preference");
mKey_preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Toast.makeText(getActivity(), "Preference 被點擊了", Toast.LENGTH_SHORT).show();
return true;
}
});
}
SharedPreferences.OnSharedPreferenceChangeListener listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// listener implementation
if (key.equals("key_EditTextPreference")) {
Preference connectionPref = findPreference(key);
// Set summary to be the user-description for the selected value
Preference key_EditTextPreference = findPreference("key_EditTextPreference");
key_EditTextPreference.setSummary(sharedPreferences.getString(key, "") + "");
}
}
};
@Override
public void onResume() {
super.onResume();
//註冊監聽
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(listener);
}
@Override
public void onPause() {
super.onPause();
//取消監聽
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(listener);
}
}
首選項標頭使用
我們知道可以用嵌套的PreferenceScreen元素構建子屏幕,但在Android 3.0(API 10)以上的版本,更建議我們我們使用”標頭”功能,好處是在大屏幕運行時,會自動提供雙窗格佈局
一、創建標頭XML文件
<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="zs.xmx.last.NormalPreferenceFragment"
android:summary="NormalPreferenceFragment"
android:title="NormalPreferenceFragment">
<!-- key/value pairs can be included as arguments for the fragment. -->
<extra
android:name="headers_key"
android:value="preference-headers 攜帶的值"/>
</header>
<header
android:fragment="zs.xmx.last.DialogPreferenceFragment"
android:summary="DialogPreferenceFragment"
android:title="DialogPreferenceFragment"/>
<header
android:fragment="zs.xmx.last.TwoStatePreferenceFragment"
android:summary="TwoStatePreferenceFragment"
android:title="TwoStatePreferenceFragment"/>
<header
android:fragment="zs.xmx.last.CustomPreferenceFragment"
android:summary="CustomPreferenceFragment"
android:title="CustomPreferenceFragment"/>
</preference-headers>
二、顯示標頭
/**
* 使用標頭,要繼承PreferenceActivity,重寫onBuildHeaders()和isValidFragment()
*/
public class SettingPreferenceActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (hasHeaders()) { //如有header,則在最下面加一個button。本例無論平板還是phone,都返回true
Button button = new Button(this);
button.setText("Exit");
setListFooter(button);
}
}
@Override
public void onBuildHeaders(List<Header> target) {
super.onBuildHeaders(target);
loadHeadersFromResource(R.xml.preference_heads, target);
}
/**
* API 19以上的安全機制,需要重寫isValidFragment()
* <p>
* 1.直接return true
* <p>
* 2.return [YOUR_FRAGMENT_NAME].class.getName().equals(fragmentName);
*
* @param fragmentName
* @return
*/
@Override
protected boolean isValidFragment(String fragmentName) {
return true;
}
==Preference注意事項==
並非我們第一次打開相應界面之後就會自動創建對應的SharedPreferences文件,而是在我們改變了原有狀態時候
並非所有的Preference及其子類都會創建,僅僅針對需要記錄狀態的Preference.
使用PreferenceActivity時,setContentView()方法的layout文件裏必須包含id爲android.R.id.list的listView,否則會報E/AndroidRuntime: Caused by: Java.lang.RuntimeException: Your content must have a ListView whose id attribute is ‘android.R.id.list’,再調用addPreferencesFromResource來完成Preference界面的構建;建議直接調用addPreferencesFromResource方法.
擴展:
使用Intent
在某些情況下,您可能需要首選項來打開不同的 Activity(而不是網絡瀏覽器等設置屏幕)或查看網頁.要在用戶選擇首選項時調用 Intent,請將 元素添加爲相應 元素的子元素.
//使用首選項打開網頁,屬性與我們Activity使用Intent類似
<Preference android:title="@string/prefs_web_page" >
<intent android:action="android.intent.action.VIEW"
android:data="http://www.example.com" />
</Preference>
保存和恢復首選項的狀態
類似Activity的橫豎屏切換恢復狀態,Preference 子類也負責保存並恢復其狀態.要正確保存並恢復 Preference 類的狀態,您必須實現生命週期回調方法 onSaveInstanceState() 和 onRestoreInstanceState().
Preference 的狀態由實現 Parcelable 接口的對象定義.Android 框架爲您提供此類對象,作爲定義狀態對象(Preference.BaseSavedState 類)的起點.
要定義 Preference 類保存其狀態的方式,您應該擴展 Preference.BaseSavedState 類.您只需重寫幾種方法並定義 CREATOR 對象.
對於大多數應用,如果 Preference 子類保存除整型數以外的其他數據類型,則可複製下列實現並直接更改處理 value 的行.
private static class SavedState extends BaseSavedState {
// Member that holds the setting's value
// Change this data type to match the type saved by your Preference
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readInt(); // Change this to read the appropriate data type
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeInt(value); // Change this to write the appropriate data type
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
如果將上述 Preference.BaseSavedState 實現添加到您的應用(通常,作爲 Preference 子類的子類),則需要爲 Preference 子類實現 onSaveInstanceState() 和 onRestoreInstanceState() 方法。
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
if (isPersistent()) {
// No need to save instance state since it's persistent,
// use superclass state
return superState;
}
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set the state's value with the class member that holds current
// setting value
myState.value = mNewValue;
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whether we saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state to custom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set this Preference's widget to reflect the restored state
mNumberPicker.setValue(myState.value);
}
總結:
本篇到這裏就完結了,喜歡的同學歡迎關注,後續會不斷更新文章,也歡迎評論,支出錯誤,共同進步