前言:偏好设置如果是负责设置模块的同学就会非常熟悉,我虽然很熟悉,但是没有系统地看过官方文档,都是看参考设置以及学习设置源码的,也许会有所缺漏。
学习:https://developer.android.google.cn/guide/topics/ui/settings
注意:本指南介绍如何使用 AndroidX Preference Library。自 Android 10 开始,系统已弃用 android.preference
库平台。
设置使用户能够改变应用的功能和行为。设置可以影响后台行为,例如应用与云同步数据的频率;设置的影响还可能更为深远,例如改变用户界面的内容和呈现方式。
建议使用 AndroidX Preference Library 将用户可配置设置集成至您的应用中。此库管理界面,并与存储空间交互,因此您只需定义用户可以配置的单独设置。此库自带 Material 主题,可在不同的设备和操作系统版本之间提供一致的用户体验。
简单入门的参考https://developer.android.google.cn/guide/topics/ui/settings
本文主要学习下之前有缺漏的部分
您可以在用户跳转至设置屏幕时,控制哪些
Preferences
对用户可见。 例如,如果某个特定Preference
仅在对应功能启用时才有意义,则您可能想在该功能停用时隐藏该Preference
。要仅在符合某项条件时显示
Preference
,首先要在 XML 中将Preference
可见性设置为 false,如以下示例所示:接下来,当符合相应条件时,在
onCreatePreferences()
中显示Preference
:KotlinJava
@Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.preferences, rootKey); if(*/\*some feature\*/*) { Preference editTextPreference = findPreference("signature"); editTextPreference.setVisible(true); } }
这边学习一下,之前项目的实现是默认显示,代码里动态判断若不支持则移除对应preference
这边官方文档提供另外一种思路,默认隐藏,动态判断支持则显示出来
无论
Preference
何时请求摘要,您都可以创建自己的SummaryProvider
,并替换provideSummary()
,以自定义摘要。 例如,下面的EditTextPreference
展示了其作为摘要保存的值的长度:举例来说,假设以下
EditTextPreference
:在
onCreatePreferences()
中,新建SummaryProvider
,然后替换provideSummary()
以返回要显示的摘要:KotlinJava
EditTextPreference countingPreference = (EditTextPreference) findPreference("counting"); countingPreference.setSummaryProvider(new SummaryProvider() { @Override public CharSequence provideSummary(EditTextPreference preference) { String text = preference.getText(); if (TextUtils.isEmpty(text)){ return "Not set"; } return "Length of saved value: " + text.length(); } }); 现在,
Preference
摘要显示保存的值的长度,如果 不存在保存的值,则显示“未设置”。
这里有代码动态根据preference的值设定summary的方法,这块没实际用过。
Preference
在点按时可执行特定操作。 例如,Preference
可以用作关联应用某个单独部分的链接。 要为Preference
添加操作,您可以直接在Preference
上设置Intent
,或设置OnPreferenceClickListener
,以获得更具体的逻辑。
您可以在
Preference
上设置Intent
,以在每次点按Preference
时,启用新的Fragment
、Activity
、或单独的应用。 这等同于使用给定Intent
的Context.startActivity()
。您可以使用嵌套
标签在 XML 中设置
Intent
。 下方示例定义了启动Activity
的Intent
:或者,您也可以在
Preference
上直接使用setIntent()
,如下所示:KotlinJava
Intent intent = new Intent(getContext(), ExampleActivity.class); activityPreference.setIntent(intent);您还可以通过 XML,添加带有
Intent
的 extras:以下为
Preference
的示例,其中带有启动网页的Intent
KotlinJava
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("http://www.google.com")); webpagePreference.setIntent(intent);
您可以在
Preference
上设置OnPreferenceClickListener
,当点按Preference
时,此操作会为onPreferenceClick()
添加回调。 例如,如果您有更复杂的逻辑可以用于处理跳转,则可以使用侦听器跳转至另一个Fragment
或Activity
。要设置
OnPreferenceClickListener
,请使用与下方所示内容类似的代码:KotlinJava
onClickPreference.setOnPreferenceClickListener(preference -> { // do something return true; });
这点击事件其实没啥说的,现在Android PreferenceController都封装好了,不用单独设置了。
默认情况下,
Preference
使用SharedPreferences
保存值。SharedPreferences
API 支持从在各个应用会话中保存的文件中,读取和写入简单的键值对。Preference Library 使用不公开的
SharedPreferences
实例,因此只有您的应用可以访问该库。举例来说,我们假设以下
SwitchPreferenceCompat
:如果用户将此开关切换至
On
状态,则SharedPreferences
文件使用"notifications" : "true"
键值对更新。 请注意,所用密钥与为Preference
设置的密钥一致。如需更多有关
SharedPreferences
API 的信息,请参阅 保存键值数据。如要详细了解在 Android 上存储数据的不同方法,请参阅 数据和文件存储概览。
但是实际上并不会用preference的这个特性存储值,因为Settings的preference的功能不光是Settings自己的事,更多的是服务于整个安卓系统,一般会保存到SettingsProvider中或者有特定的api接口可以设置和读取。
要检索正在使用的
SharedPreferences
对象,请调用PreferenceManager.getDefaultSharedPreferences()
。 此方法适用于您应用的任何位置。 例如,给定一个密钥为“signature”的EditTextPreference
:您可以对此
Preference
的保存值进行全局检索,方法如下:KotlinJava
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this /* Activity context */); String name = sharedPreferences.getString(“signature”, "");
这个最近刚用到过,这个方法本质上是拼接了包名等信息生成SharedPreference的xml保存到应用目录下。
要侦听 Preference
值的变化,您可以在两个接口中选择一个:
Preference.OnPreferenceChangeListener
SharedPreferences.OnSharedPreferenceChangeListener
下表展示了两个接口的差别:
OnPreferenceChangeListener | OnSharedPreferenceChangeListener |
---|---|
针对每个首选项进行设置 | 应用于所有首选项 |
在首选项要改变其保存的值时进行调用。 如果待处理的值与当前保存的值相同,则其也包括在内。 | 只在为首选项保存的值发生变化时调用。 |
只通过 Preference Library 调用。 应用的单独部分可以改变保存的值。 | 随保存值的变动进行调用,即便该值来自应用的单独部分也是如此。 |
在待处理的值保存前调用。 | 在该值保存后调用。 |
在使用 SharedPreferences 或 PreferenceDataStore 时调用。 |
只在使用 SharedPreferences 时调用。 |
执行
OnPreferenceChangeListener
使您能够侦听Preference
的值将在何时出现变动。 由此,您可以验证此变动是否应该发生。 例如,以下代码展示了如何侦听密钥为“name”的EditTextPreference
值的变化:KotlinJava
@Override public boolean onPreferenceChange(Preference preference, Object newValue) { Log.e("preference", "Pending Preference value is: " + newValue); return true; }接下来,您需要直接使用
setOnPreferenceChangeListener()
设置此侦听器,如下所示:KotlinJava
preference.setOnPreferenceChangeListener(...);
在使用
SharedPreferences
保留Preference
值时,您还可以使用SharedPreferences.OnSharedPreferenceChangeListener
侦听变动。 这使您能够侦听Preference
保存的值在何时出现变动,例如在与服务器同步设置时。 以下示例展示了如何侦听密钥为“name”的EditTextPreference
值 出现变动的时间:KotlinJava
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(“signature”)) { Log.i(TAG, “Preference value was updated to: “ + sharedPreferences.getString(key, "")); } }此外,您必须通过
registerOnSharedPreferenceChangedListener()
注册侦听器,如下所示:KotlinJava
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(...);警告:为避免意外的垃圾回收,您必须存储对侦听器的强引用。 在您调用
registerOnSharedPreferenceChangeListener()
时,SharedPreferenceManager
不会存储对侦听器的强引用。 为解决这一问题,您可直接在PreferenceFragmentCompat
中执行onSharedPreferenceChanged()
。 您还可以创建一个实例变量,如下所示:KotlinJava
SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {...}为妥善管理
Activity
或Fragment
的生命周期,您应使用onResume()
和onPause()
回调完成此侦听器的注册和取消注册,如下所示:KotlinJava
@Override public void onResume() { super.onResume(); getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); } @Override public void onPause() { super.onPause(); getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); }
学习了下OnSharedPreferenceChangeListener的使用,可以监听preference的总体变化,但感觉没啥用。。。
虽然推荐使用 SharedPreferences
保留 Preference
,但您也可以使用自定义数据存储。 如果您的应用将值保留至数据库,又或者值专门针对设备,这时自定义数据存储会非常有用,如示例所示。
要采用自定义数据存储,先要创建一个扩展
PreferenceDataStore
的类。 以下示例创建了一个处理String
值的数据存储:KotlinJava
public class DataStore extends PreferenceDataStore { @Override public void putString(String key, @Nullable String value) { // Save the value somewhere } @Override @Nullable public String getString(String key, @Nullable String defValue) { // Retrieve the value } }请注意:只需替换您
Preference
对象所使用的方法。 试图调用您尚未实现的方法会导致UnsupportedOperationException
。确保不在主线程上执行任何耗时操作,以避免堵塞用户接口。 由于在保留值的过程中,包含数据存储的
Fragment
或Activity
可能会被销毁,所以您应该将数据序列化,以防丢失用户更改的任何值。
采用数据存储后,您必须在
onCreatePreferences()
中设置新的数据存储,以便Preference
对象使用数据存储保留值,而非使用默认的SharedPreferences
。 您可以针对每个Preference
或整个层次结构启用数据存储。要为特定
Preference
启用自定义数据存储,请调用Preference
上的setPreferenceDataStore()
,如以下示例所示:KotlinJava
Preference preference = findPreference(“key”); preference.setPreferenceDataStore(dataStore);要为整个层次结构启用自定义数据存储,请调用
PreferenceManager
上的setPreferenceDataStore()
:KotlinJava
PreferenceManager preferenceManager = getPreferenceManager(); preferenceManager.setPreferenceDataStore(dataStore);为特定
Preference
设置的数据存储将替换为对应层次结构设置的任何数据存储。 大多数情况下,您需要为整个层次结构设置一个数据存储。请注意:如果您在
Preference
附加到层次结构后,再为某个Preference
设置数据存储,则Preference
的初始值将不再传播。
这里的PreferenceDataStore看起来自定义了数据存储,但是不可避免的将一堆preference的数据存储放在了一个类里,然后可以预见到一堆if else的代码结构,没有现在Android用的PreferenceController好