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