目录
1.从PreferenceScreen 中 删除某个Preference
3. Activity&Fragment / Preference&Fragment 组合使用
5. Preference 点击回调事件(OnPreferenceClickListener 、OnPreferenceChangeListener)
1.从PreferenceScreen 中 删除某个Preference
(1) . 先获取PreferenceScreen
PreferenceScreen ps = getPreferenceScreen();
(2). 获取要删除的Preference
Preference pf = findPreference(pfKey); // pfKey 为String类型
3. 删除
if (pf != null && ps != null) {
ps.removePreference(pf);
}
其中removePreference 会返回 boolean 值, 删除成功true, 否正false
从 PreferenceCategory 中删除Preference 也是类似,先要获取PreferenceCategory
2. Preference类图
(1) PreferenceGroup 是Preference 的子类,也是一个抽象类,可以拥有一个或者多个Preference, 类似View 和ViewGroup.
(2) PreferenceScreen 和PreferenceCategory 是PreferenceGroup的子类,扩展少量方法
(3) PreferenceScreen 作为xml 布局文件根标签时,其设置的Titile 是不会显示的(需要设置主题支持Actionbar类型的,才会显示)
PreferenceScreen 内部再嵌套一个PreferenceScreen时(组合), 内部的PreferenceScreen会显示Titile(点跳转后!!!), 但是其内部的Preference需要点击后才会显示出来
如:
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
<!--Title 不显示-->
android:title="WiFi"
android:key="first_preferencescreen">
<CheckBoxPreference
android:key="wifi enabled"
android:title="WiFi" />
<PreferenceScreen
android:key="second_preferencescreen"
<!--Title 显示-->
android:title="WiFi settings">
<!--内部Preference 不显示!!!! 需要点击进入后再显示Prefer wifi-->
<CheckBoxPreference
android:key="prefer wifi"
android:title="Prefer WiFi" />
</PreferenceScreen>
</PreferenceScreen>
(4) 使用PreferenceCategory 可以显示标题, 并且Title这一行不能点击,因为PreferenceCategory内部覆写
public boolean isEnabled() {
return false;
}
更多详细可以参考 https://blog.youkuaiyun.com/weixin_31767183/article/details/77748421
3. Activity&Fragment / Preference&Fragment 组合使用
其中,PreferenceFragmentCompat 可以自定义定制化的风格,作为父类提供一个app或者一个系统使用,使得风格统一。
(通过依赖库的方式编译进去,一样可以是androidx.preference的包名)
4. Actionbar (标题: 显示与隐藏 )
标题是否显示 与 它的主题 (Theme有关)
4.1 AppCompatActivity 默认有标题
/**
* Support library version of {@link android.app.Activity#getActionBar}.
*
* <p>Retrieve a reference to this activity's ActionBar.
*
* @return The Activity's ActionBar, or null if it does not have one.
*/
@Nullable
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
有两种方式隐藏标题
一种是在AndroidManifest 配置NoActionBar主题, 如 android:theme="@style/Theme.AppCompat.NoActionBar"
<activity
android:name=".XXXXActivity"
android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
另一种是在代码里配置, 如
getSupportActionBar().hide()
4.2 PreferenceActivity 默认没有标题
如果要显示标题, 在代码里配置主题 (可以引用android 库里的主题)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
if(Build.VERSION.SDK_INT> Build.VERSION_CODES.HONEYCOMB_MR2){
this.setTheme(android.R.style.Theme_DeviceDefault);
}
}
5. Preference 点击回调事件(onClick /OnPreferenceClickListener 、OnPreferenceChangeListener)
在 onBindViewHolder 的时候,设置 onClickListener, 这个listener 其实就是实现 View 的OnClickListener, Preference 内部交给performClick 处理。 详见后面
private final View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
performClick(v);
}
};
public void onBindViewHolder(PreferenceViewHolder holder) {
View itemView = holder.itemView;
Integer summaryTextColor = null;
itemView.setOnClickListener(mClickListener);
提供点击监听的回调接口给客户端, 客户端可以决定是否响应事件 以及 在更新Preference 前进行设置操作(如设置Summary)
通过返回true 或者 false 告诉系统。 (例如 客户端在CheckBoxPreference中设置了监听, 实现了OnPreferenceChangeListener, 但是返回值为false, 则不会进行更新操作checked)
/**
* Interface definition for a callback to be invoked when a {@link Preference} is
* clicked.
*
* @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/preference/package-summary.html">
* Preference Library</a> for consistent behavior across all devices.
* For more information on using the AndroidX Preference Library see
* <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
@Deprecated
public interface OnPreferenceClickListener {
/**
* Called when a Preference has been clicked.
*
* @param preference The Preference that was clicked.
* @return True if the click was handled.
*/
boolean onPreferenceClick(Preference preference);
}
/**
* Interface definition for a callback to be invoked when the value of this
* {@link Preference} has been changed by the user and is
* about to be set and/or persisted. This gives the client a chance
* to prevent setting and/or persisting the value.
*
* @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/preference/package-summary.html">
* Preference Library</a> for consistent behavior across all devices.
* For more information on using the AndroidX Preference Library see
* <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
@Deprecated
public interface OnPreferenceChangeListener {
/**
* Called when a Preference has been changed by the user. This is
* called before the state of the Preference is about to be updated and
* before the state is persisted.
*
* @param preference The changed Preference.
* @param newValue The new value of the Preference.
* @return True to update the state of the Preference with the new value.
*/
boolean onPreferenceChange(Preference preference, Object newValue);
}
performClick 里的逻辑, 如果有为Preference 设置intent ,则会调用startActivity 跳转
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void performClick() {
if (!isEnabled() || !isSelectable()) {
return;
}
onClick();
if (mOnClickListener != null && mOnClickListener.onPreferenceClick(this)) {
return;
}
PreferenceManager preferenceManager = getPreferenceManager();
if (preferenceManager != null) {
PreferenceManager.OnPreferenceTreeClickListener listener = preferenceManager
.getOnPreferenceTreeClickListener();
if (listener != null && listener.onPreferenceTreeClick(this)) {
return;
}
}
if (mIntent != null) {
Context context = getContext();
context.startActivity(mIntent);
}
}
6. persistent 是否存储在内存中(android:persistent="true")
<!-- Whether the Preference stores its value to the storage. -->
<attr name="persistent" />
<!-- Flag to control special persistent mode of an application. This should
not normally be used by applications; it requires that the system keep
your application running at all times. -->
<attr name="persistent" format="boolean" />
如果是false, 则退出Preference 后,再进入, 值不会保存。
6.1 CheckBoxPreference(TwoStatePreference)
在响应onClick 的时候,会回调(如果有注册监听), 并在setChecked的时候,调用persistXXX(例如 persistBoolean) 进行存储。
因此使用Preference 后, 会对其xml 定义的key值进行存储, 不需要手动调用SharePreference.edit.putXXX(key, value).apply 进行存储。
默认值可以用 android.defaultValue 设置, 会存储到Preference 对应的SharePreference中
//TwoStatePreference.java
@Override
protected void onClick() {
super.onClick();
final boolean newValue = !isChecked();
if (callChangeListener(newValue)) {
setChecked(newValue);
}
}
/**
* Sets the checked state and saves it to the {@link SharedPreferences}.
*
* @param checked The checked state.
*/
public void setChecked(boolean checked) {
// Always persist/notify the first time; don't assume the field's default of false.
final boolean changed = mChecked != checked;
if (changed || !mCheckedSet) {
mChecked = checked;
mCheckedSet = true;
persistBoolean(checked);
if (changed) {
notifyDependencyChange(shouldDisableDependents());
notifyChanged();
}
}
}
//Preference.java
/**
* Attempts to persist a boolean if this Preference is persistent.
*
* @param value The value to persist.
* @return True if this Preference is persistent. (This is not whether the
* value was persisted, since we may not necessarily commit if there
* will be a batch commit later.)
* @see #persistString(String)
* @see #getPersistedBoolean(boolean)
*/
protected boolean persistBoolean(boolean value) {
if (!shouldPersist()) {
return false;
}
if (value == getPersistedBoolean(!value)) {
// It's already there, so the same as persisting
return true;
}
PreferenceDataStore dataStore = getPreferenceDataStore();
if (dataStore != null) {
dataStore.putBoolean(mKey, value);
} else {
SharedPreferences.Editor editor = mPreferenceManager.getEditor();
editor.putBoolean(mKey, value);
tryCommit(editor);
}
return true;
}
6.2 普通Preference 存储字符串
/**
* Attempts to persist a String if this Preference is persistent.
*
* @param value The value to persist.
* @return True if this Preference is persistent. (This is not whether the
* value was persisted, since we may not necessarily commit if there
* will be a batch commit later.)
* @see #getPersistedString(String)
*/
protected boolean persistString(String value) {
if (!shouldPersist()) {
return false;
}
// Shouldn't store null
if (TextUtils.equals(value, getPersistedString(null))) {
// It's already there, so the same as persisting
return true;
}
PreferenceDataStore dataStore = getPreferenceDataStore();
if (dataStore != null) {
dataStore.putString(mKey, value);
} else {
SharedPreferences.Editor editor = mPreferenceManager.getEditor();
editor.putString(mKey, value);
tryCommit(editor);
}
return true;
}
读取存储的字符串
/**
* Attempts to get a persisted String if this Preference is persistent.
*
* @param defaultReturnValue The default value to return if either this
* Preference is not persistent or this Preference is not present.
* @return The value from the data store or the default return
* value.
* @see #persistString(String)
*/
protected String getPersistedString(String defaultReturnValue) {
if (!shouldPersist()) {
return defaultReturnValue;
}
PreferenceDataStore dataStore = getPreferenceDataStore();
if (dataStore != null) {
return dataStore.getString(mKey, defaultReturnValue);
}
return mPreferenceManager.getSharedPreferences().getString(mKey, defaultReturnValue);
}
7. SharedPreferences
是一个接口???
根据同一个Context 获取到的SharePreference, 对命名相同key赋值或者读取时,实际是对同一个Key操作
public class ContextWrapper extends Context {
@UnsupportedAppUsage
Context mBase;
//....
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}
}
8. 自定义Preference (如添加一个Preference 的属性值)
8.1 attrs.xml 添加自定义类型(declare-styleable)
其中,entryValue 为属性值, groupKey 则可以拓展为当前Preference 属于哪一个Group 组
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="XXXPreference">
<attr name="entryValue" format="string"></attr>
<attr name="groupKey" format="string"></attr>
</declare-styleable>
</resources>
8.2 获取自定义的属性值
通过TypedArray
public XXXPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.XXXPreference, 0, 0);
String key = getKey();
if (key != null) {
mKey = key;
}
mGroupKey = a.getText(R.styleable.RadioPreference_groupKey).toString();
mValue = a.getText(R.styleable.RadioPreference_entryValue).toString();
a.recycle();
}
注意:XML 定义的Preference 的默认构造函数是
public XXXGroupPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
setWidgetLayoutResource(R.layout.radiogroup);
mContext = context;
}
其中 radiogroup 为自定义的布局
8.3 XML 文件使用自定义Preference
其中app:entryValue 和 app:groupKey 即为自定义的属性 (xml文件的根标签 需要添加工具 xmlns:app="http://schemas.android.com/apk/res-auto")
<com.example.Test.XXXPreference
android:key="@string/pref_test"
android:defaultValue="false"
android:persistent="true"
app:entryValue="@string/val_test_value"
app:groupKey="@string/pref_test_group"
android:title="@string/title_test" />
这样,可以为Preference 新增一个属性,并且可以在XML 使用时赋值, 简单明了。
(也可以在定义一个数组,在代码中为各个Preference的自定义属性赋值)