valenhua的专栏

Android Preference 设置偏好全攻略

2017-09-29

Android 设置是每个App必不可小的东西,看似很简单,但是初学不熟悉的很花时间去研究,特别样式兼容方面,以及有自定义设置的需求,下面是对用法做一个总结

Preference结构

界面结构看下图


界面主要由PrefercenScreen、PreferenceCategory和Preference三个主要部分组成

  • PrefercenScreen最根的部分;
  • PreferenceCategory是每个设置的分组;
  • Preference是具体到每个设置元素;
    XML文件
    XML结构层级大致如图
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <android.support.v7.preference.PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">
    <android.support.v7.preference.PreferenceCategory
    android:title="preference v7">
    <android.support.v7.preference.CheckBoxPreference
    android:defaultValue="false"
    android:key="key_checkbox"
    android:summaryOff="@string/theme_light"
    android:summaryOn="@string/theme_dark"
    android:title="Checkbox"
    />
    </android.support.v7.preference.PreferenceCategory>
    </android.support.v7.preference.PreferenceScreen>

类库

由于Android版本升级,Preference加上原生的供有四个版本类库,根据API的最低版本使用相应的类库,如果你的最低版本是11,使用PreferenceFragment的话就可不使用兼容库。下面对每个包层次整理一下

android.preference

PreferenceFragment
如果你的安卓最低版本面向11以上的,可以直接使用这个包为基础

android.support.v7.preference

android.support.v14.preference

android.support.v17.preference

这个包主要面向电视,暂时不深入研究,具体特性迟点再补充

Preference介绍

Preference
  • Preference 是所有设置项目的基类,很多属性都从这个类派生出来。
    它有android.preference.Preference和 android.support.v7.preference.Preference两个部分,大致用法相同,详细请参考文档;

  • 从文档可了解到android.preference.Preference 是针对PreferenceActivity
    ListView旧款偏好而设计,而android.support.v7.preference.Preference则适合
    用RecyclerView重新设计的PreferenceFragmentCompat,使用的时候对号入座就可以了;

  • 他们都是内置 SharedPreferences 以及对应的android:key属性来存储数据

关键属性:

  • android:title,显示的标题;
  • android:summary,标题下面对应的摘要;
  • android:key,存储数据的键值;
  • android:dependency: 这是一个键值,如果设置,必须依赖key这个才可以启用;
PreferenceScreen

它是所有偏好的根节点:

  • 当它放在根部的时候是不显示的;
  • 当它放在节点位置的显示,点击显示它的子元素;
    如图:


    PreferenceCategory
    这个很简单就是一个分组的效果,只要设置好标题就可以了
    MultiSelectListPreference
    显示多选对话框,存储多项数据,关键属性:
  • android:entries,显示栏目数据
  • android:entryValues,栏目对应的值
  • android:defaultValue 默认值,数组

通过上面的架构发现,到v7下面没有了MultiSelectListPreference,只有
MultiSelectListPreferenceDialogFragmentCompat,原因不得而知,估计是google想只提供对话框给开发者自己实现,但是到了v14下面MultiSelectListPreference回归了,原因不深入,直接使用就可以。

1
2
3
4
5
6
7
8
9
<MultiSelectListPreference
android:defaultValue="@array/deflistItems"
android:dialogTitle="MultiSelectListPreferenceDialog"
android:entries="@array/listItems"
android:entryValues="@array/listItems"
android:key="key_multi_select_list_pref"
android:summary="MultiSelectListPreference summary"
android:title="MultiSelectListPreference"
/>

效果图

ListPreference

显示单选对话框,存储单项数据,关键属性:

  • android:entries,显示栏目数据
  • android:entryValues,栏目对应的值
  • android:defaultValue 默认值

ListPreference 存在原生和v7包下面,v14只保留ListPreferenceDialogFragment

1
2
3
4
5
6
7
8
9
<ListPreference
android:defaultValue="Item4"
android:dialogTitle="ListPreferenceDialog"
android:entries="@array/listItems"
android:entryValues="@array/listItems"
android:key="key_list_pref"
android:summary="ListPreference summary"
android:title="ListPreference"
/>

效果图:

EditTextPreference

EditTextPreference继承DialogPreference,然后编辑对户框可以输入文字并保存
EditTextPreference存在原生和v7包下面,v14只保留EditTextPreferenceDialogFragment
关键属性:

  • android:defaultValue 默认值
1
2
3
4
5
6
<EditTextPreference
android:defaultValue="defaultValue"
android:key="key_edittext"
android:summary="EditTextPreference summary"
android:title="EditTextPreference"
/>

效果图

CheckBoxPreference

选择框偏好,CheckBoxPreference存在原生和v7包下面
关键属性:

  • android:defaultValue 默认值
  • android:summaryOff: 取消选择的时候显示文字
  • android:summaryOn:选择的时候显示文字
1
2
3
4
5
6
7
<CheckBoxPreference
android:defaultValue="false"
android:key="key_checkbox"
android:summaryOff="@string/theme_light"
android:summaryOn="@string/theme_dark"
android:title="Checkbox"
/>

效果图

SwitchPreference

开关编好,使用也比较简单,原生和v14都有,v7下面是SwitchPreferenceCompat
关键属性:

  • android:defaultValue 默认值
  • android:summaryOff: 关闭时候显示文字
  • android:summaryOn: 开启时候显示文字
  • android:switchTextOff: 关闭时候开关上显示的文字
  • android:switchTextOn:开启时候开关上显示的文字
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <SwitchPreference
    android:defaultValue="false"
    android:switchTextOff="@string/theme_light"
    android:switchTextOn="@string/theme_dark"
    android:summaryOff="@string/theme_light"
    android:summaryOn="@string/theme_dark"
    android:title="SwitchPreference"/>
    <android.support.v7.preference.SwitchPreferenceCompat
    android:defaultValue="false"
    android:summaryOff="@string/theme_light"
    android:summaryOn="@string/theme_dark"
    android:switchTextOff="@string/theme_light"
    android:switchTextOn="@string/theme_dark"
    android:title="SwitchPreferenceCompat"/>
    <android.support.v14.preference.SwitchPreference
    android:defaultValue="false"
    android:key="key_dark_theme_v14"
    android:switchTextOff="@string/theme_light"
    android:switchTextOn="@string/theme_dark"
    android:summaryOff="@string/theme_light"
    android:summaryOn="@string/theme_dark"
    android:title="v14.preference.SwitchPreference"/>

经过测试,SwitchPreferenceCompat 全平台material design风格,开关样式如图看出
android.support.v14.preference.SwitchPreference和SwitchPreference Android5.0以下,按钮还是保留老样式,可以显示文字

效果图

RingtonePreference

铃声选择编好,只存在原生包上面
关键属性:
android:ringtoneType 铃声类型分四个选项:

  • alarm:只显示闹钟声
  • all: 显示所有
  • notification: 只显示通知声
  • ringtone 只显示铃声
    android:showDefault: 是否显示默认选项,默认显示

    android:showSilent 是否显示静音选项,默认显示

    效果:
    SeekBarPreference
    微调偏好设置,SeekBarPreference只在v7包下存在
    关键属性
    app:showSeekBarValue: 是否显示进度数字
    android:defaultValue: 默认值
    1
    2
    3
    4
    5
    6
    <android.support.v7.preference.SeekBarPreference
    android:key="key_seekbar"
    android:title="SeekBarPreference"
    app:showSeekBarValue="false"
    android:defaultValue="50"
    />

和ListPreference差不多,只不过是用下拉的样式代替对话框

1
2
3
4
5
6
7
8
<android.support.v7.preference.DropDownPreference
android:defaultValue="Item4"
android:dialogTitle="DropDownDialog"
android:entries="@array/listItems"
android:entryValues="@array/listItems"
android:summary="DropDownPreference summary"
android:title="DropDownPreference"
/>

效果图:

Preference自定义

以上偏好都不能满足的时候需要自定义,例如我的App软件上定义了一个关于的对话框
先看看效果是这样的:

  • 我们需要定义preference,通过继承自定义DialogPreferece,代码如下
1
2
3
4
5
6
7
8
9
public class AboutDialogPreference extends DialogPreference {
public static final String KEY_ABOUT_DIALOG = "key_about_dialog";
public AboutDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setKey(KEY_ABOUT_DIALOG);
setTitle(R.string.about);
setSummary(getContext().getString(R.string.app_name) + PkgUtils.get(getContext()).getAppVerName());
}
}
  • 然后自定义对话框
    关键一点,newInstance()必须带上AboutDialogPreference的key,否则会出错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class AboutPreferenceDialogFragment extends PreferenceDialogFragmentCompat {
    public static AboutPreferenceDialogFragment newInstance() {
    Bundle args = new Bundle();
    args.putString(ARG_KEY, AboutDialogPreference.KEY_ABOUT_DIALOG);
    AboutPreferenceDialogFragment fragment = new AboutPreferenceDialogFragment();
    fragment.setArguments(args);
    return fragment;
    }
    @Override
    protected View onCreateDialogView(Context context) {
    return View.inflate(context, R.layout.fragment_preference_about_dialog, null);
    }
    @Override
    protected void onBindDialogView(View view) {
    AppCompatTextView messageView = (AppCompatTextView) view;
    messageView.setText(getInfo());
    }
    private String getInfo() {
    String systemInfo = getString(R.string.model) + " " + Build.MODEL + "\n"
    + getString(R.string.system) + " Android "
    + Build.VERSION.RELEASE + "(API:" + Build.VERSION.SDK_INT + ")";
    PackageInfo info = PkgUtils.get(getContext()).getPackageInfo(getContext().getPackageName());
    String pkg = info != null ? info.versionName + (BuildConfig.DEBUG ? " debug" : "") : "";
    return getString(R.string.about_intro, getString(R.string.app_name) + pkg, systemInfo);
    }
    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
    builder.setIcon(R.mipmap.ic_launcher);
    builder.setTitle(R.string.about);
    builder.setNeutralButton(R.string.privacy_policy, (dialogInterface, i) -> {
    TXWebViewActivity.browse(getContext(),
    getContext().getString(R.string.privacy_policy),
    TXApplication.URL_PRIVACY, false);
    });
    builder.setPositiveButton(R.string.app_detail, (dialogInterface, i) -> { IntentUtils.get(getContext()).showAppDetails(getContext().getPackageName());
    });
    }
    @Override
    public void onDialogClosed(boolean positiveResult) {
    }
    }
  • 最后在SettingFragment主界面做处理
    在onDisplayPreferenceDialog里面判断并且实例化AboutPreferenceDialogFragment,然后显示对话框

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class SettingFragment extends PreferenceFragmentCompat{
    @Override
    public void onDisplayPreferenceDialog(Preference preference) {
    DialogFragment dialogFragment = null;
    if (preference instanceof AboutDialogPreference) {
    dialogFragment = AboutPreferenceDialogFragment.newInstance();
    }
    if (dialogFragment != null) {
    dialogFragment.setTargetFragment(this, 0);
    dialogFragment.show(this.getFragmentManager(), "");
    } else {
    super.onDisplayPreferenceDialog(preference);
    }
    }
    }

总结

  • Preference掌握关键必须理清类的集成关系,主要是Preference,分对话框Preference和普通双态Preference,并且掌握他们的扩展的
  • PreferenceActivity和PreferenceFragment模式的差异
  • 理清每个支持库的差异,多在模拟器实践验证
  • 掌握关键属性,多看文档
Tags: Android
使用支付宝打赏
使用微信打赏

若文章能帮助到你,点击上方按钮打赏,买杯咖啡和继续创作的动力

扫描二维码,分享此文章