导读
最近做项目无意中看到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);
}
总结:
本篇到这里就完结了,喜欢的同学欢迎关注,后续会不断更新文章,也欢迎评论,支出错误,共同进步