最近在升级替换使用 androidx DialogPreference 的时候,发现androidx 对DialogPreference进行了解耦,之前的数据持久化和Ui的绑定在DialogPreference中就能实现,而现在需要通过PreferenceDialogFragmentCompat和DialogPreference配合才能使用。
说实话对preference的应用在做这个升级之前我并没有遇见过,所以,只能摸索着去探索。
1.首先分析下这两个类要怎么使用
参考文档:https://medium.com/@JakobUlbrich/building-a-settings-screen-for-android-part-3-ae9793fd31ec
首先看一张图:

这张图是主要的架构图:(以一个设置页面为例子)
1.PreferenceDialogFragmentCompat:点击DialogPrecence的时候展示的 Dialog
2.Preference:显示在设置页面上的item,同时也是数据持久化的容器
3.PreferenceFragmentCompat:展示设置的主要Fragment
4.PreferenceManager:sharePreference(Preference)和PreferenceFragmentCompat的数据管理器,两个类共享这个Manager
主要的流程:
1.preference 点击--> 调用onclick方法
protected void onClick() {
getPreferenceManager().showDialog(this);
}
这时候会通过PrefereceManager去调用 showDialog方法
*/
public void showDialog(Preference preference) {
if (mOnDisplayPreferenceDialogListener != null) {
mOnDisplayPreferenceDialogListener.onDisplayPreferenceDialog(preference);
}
}
2.传递事件到Fragment:
通过listener传递出去,在这边我们很容易猜到 PreferenceFragmentCompat 实现了 DisplayPreferenceDialogListener
到时候事件通过监听传递到PreferenceFragmentCompat
3.PreferenceFragmentCompat 实现 display方法
public void onStart() {
super.onStart();
mPreferenceManager.setOnPreferenceTreeClickListener(this);
mPreferenceManager.setOnDisplayPreferenceDialogListener(this);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
boolean handled = false;
if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) {
handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment())
.onPreferenceDisplayDialog(this, preference);
}
if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) {
handled = ((OnPreferenceDisplayDialogCallback) getActivity())
.onPreferenceDisplayDialog(this, preference);
}
if (handled) {
return;
}
// check if dialog is already showing
if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
return;
}
final DialogFragment f;
if (preference instanceof EditTextPreference) {
f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey());
} else if (preference instanceof ListPreference) {
f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
} else if (preference instanceof AbstractMultiSelectListPreference) {
f = MultiSelectListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
} else {
throw new IllegalArgumentException("Tried to display dialog for unknown " +
"preference type. Did you forget to override onDisplayPreferenceDialog()?");
}
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
默认的实现方法中 会根据preference的不同来使用显示不同的Dialog 也就是说dialogPrefece和DialogFragmentCompact都是配套使用的
我们在重写display方法的时候页应该根据不同的prefence去显示不同的dialog
问题:Preference和Fragment之间是怎么怎么关联起来的?
1.看源码:可以看出来,FragmentManager会在inflate xml的时候,attach到Preference,这个就不多说了
然后接下来是Prefence和PreferenceDialogFragment之间的联系
我们先从getPeference这个方法入手:
public DialogPreference getPreference() {
if (mPreference == null) {
final String key = getArguments().getString(ARG_KEY);
final DialogPreference.TargetFragment fragment =
(DialogPreference.TargetFragment) getTargetFragment();
mPreference = (DialogPreference) fragment.findPreference(key);
}
return mPreference;
}
这边的流程是:首先根据key找到一个TargeFragment,然后根据这个通过TargeFragment去找到一个preference
从这边我们很容易猜到 TargetFragment其实就是PreferenceFragmentCompact。也就说 PreferenceDialogFragmentCompact和PreferenceFragment是绑定在一起的,跟人觉得这样设计就是为了把之前的数据绑定,转化成UI的绑定。
从这段代码我们也可以看到setTarget 和设置key是必不可少的一步操作,不然,就无法将各个元素关联起来(之前在这边踩过坑)。而这边的key和关键,其实就是绑定的preference的key
@SuppressWarnings("ReferenceEquality")
public void setTargetFragment(@Nullable Fragment fragment, int requestCode) {
// Don't allow a caller to set a target fragment in another FragmentManager,
// but there's a snag: people do set target fragments before fragments get added.
// We'll have the FragmentManager check that for validity when we move
// the fragments to a valid state.
final FragmentManager mine = getFragmentManager();
final FragmentManager theirs = fragment != null ? fragment.getFragmentManager() : null;
if (mine != null && theirs != null && mine != theirs) {
throw new IllegalArgumentException("Fragment " + fragment
+ " must share the same FragmentManager to be set as a target fragment");
}
// Don't let someone create a cycle.
for (Fragment check = fragment; check != null; check = check.getTargetFragment()) {
if (check == this) {
throw new IllegalArgumentException("Setting " + fragment + " as the target of "
+ this + " would create a target cycle");
}
}
mTarget = fragment;
mTargetRequestCode = requestCode;
}
那现在整个流程就都连起来了。接下去看下:PreferenceDialogFragmentCompact的创建过程,有哪几个关键的操作
@Override
public @NonNull
Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
View contentView = onCreateDialogView(context);
if (contentView != null) {
onBindDialogView(contentView);
builder.setView(contentView);
} else {
builder.setMessage(mDialogMessage);
}
onPrepareDialogBuilder(builder);
// Create the dialog
final Dialog dialog = builder.create();
if (needInputMethod()) {
requestInputMethod(dialog);
}
return dialog;
}
从createDialog入手,流程大概是:1.创建一个 Dialog -->2.oncreateDialogView 创建一个ContentView ->3.onBindViewHolder对数据进行绑定->onPrepareDialogBuilder 对dialog的参数进行进一步的设置--> 设置是否显示键盘-->然后返回Dialog
所以我们所做的操作就是在子类重写几个方法:
1.onCreateDialogView中设置contView的样式
2.onBindViewHolder 去通过Preference的数据进行数据的绑定
3. onPrepareDialogBuilder去进一步设置Dialog,这样就完成了Dialog的创建
问题:如果想设置Dialog positiveButton的Enable属性,要怎么设置,Builder并没有这样的方法。
解决办法:
子类重写oncreateDialog
获取super已经创建好的dialog,然后对dialog的参数进一步设置,如设置showListener等等,紧接着再返回
具体的例子:上面的参考文档有写
<?xml version="1.0" encoding="utf-8"?>
<TimePicker
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/alert_def_padding"
android:paddingBottom="@dimen/alert_def_padding"
android:theme="@style/AppAlertDialogContent" />
import android.support.v7.preference.DialogPreference;
public class TimePreference extends DialogPreference {
...
}
private int mTime;
private int mDialogLayoutResId = R.layout.pref_dialog_time;
public TimePreference(Context context) {
this(context, null);
}
public TimePreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimePreference(Context context, AttributeSet attrs,
int defStyleAttr) {
this(context, attrs, defStyleAttr, defStyleAttr);
}
public TimePreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// Do custom stuff here
// ...
// read attributes etc.
}
public int getTime() {
return mTime;
}
public void setTime(int time) {
mTime = time;
// Save to Shared Preferences
persistInt(time);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
// Default value from attribute. Fallback value is set to 0.
return a.getInt(index, 0);
}
@Override
protected void onSetInitialValue(boolean restorePersistedValue,
Object defaultValue) {
// Read the value. Use the default value if it is not possible.
setTime(restorePersistedValue ?
getPersistedInt(mTime) : (int) defaultValue);
}
public static TimePreferenceDialogFragmentCompat newInstance(
String key) {
final TimePreferenceDialogFragmentCompat
fragment = new TimePreferenceDialogFragmentCompat();
final Bundle b = new Bundle(1);
b.putString(ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mTimePicker = (TimePicker) view.findViewById(R.id.edit);
// Exception when there is no TimePicker
if (mTimePicker == null) {
throw new IllegalStateException("Dialog view must contain" +
" a TimePicker with id 'edit'");
}
// Get the time from the related Preference
Integer minutesAfterMidnight = null;
DialogPreference preference = getPreference();
if (preference instanceof TimePreference) {
minutesAfterMidnight =
((TimePreference) preference).getTime();
}
// Set the time to the TimePicker
if (minutesAfterMidnight != null) {
int hours = minutesAfterMidnight / 60;
int minutes = minutesAfterMidnight % 60;
boolean is24hour = DateFormat.is24HourFormat(getContext());
mTimePicker.setIs24HourView(is24hour);
mTimePicker.setCurrentHour(hours);
mTimePicker.setCurrentMinute(minutes);
}
}
@Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
// generate value to save
int hours = mTimePicker.getCurrentHour();
int minutes = mTimePicker.getCurrentMinute();
int minutesAfterMidnight = (hours * 60) + minutes;
// Get the related Preference and save the value
DialogPreference preference = getPreference();
if (preference instanceof TimePreference) {
TimePreference timePreference =
((TimePreference) preference);
// This allows the client to ignore the user value.
if (timePreference.callChangeListener(
minutesAfterMidnight)) {
// Save the value
timePreference.setTime(minutesAfterMidnight);
}
}
}
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// Try if the preference is one of our custom Preferences
DialogFragment dialogFragment = null;
if (preference instanceof TimePreference) {
// Create a new instance of TimePreferenceDialogFragment with the key of the related
// Preference
dialogFragment = TimePreferenceDialogFragmentCompat
.newInstance(preference.getKey());
}
// If it was one of our cutom Preferences, show its dialog
if (dialogFragment != null) {
dialogFragment.setTargetFragment(this, 0);
dialogFragment.show(this.getFragmentManager(),
"android.support.v7.preference" +
".PreferenceFragment.DIALOG");
}
// Could not be handled here. Try with the super method.
else {
super.onDisplayPreferenceDialog(preference);
}
}
今天的的源码分析就在这里了,其实这一个系列的使用以前也没怎么接触过,这次也是边学习边写,接下去用空再继续深入学习

本文介绍了在升级到androidx后,如何使用PreferenceDialogFragmentCompat和DialogPreference配合实现数据持久化和UI绑定。文章详细阐述了两者的使用流程,包括点击Preference触发显示Dialog、事件传递、数据绑定以及关键方法的重写,特别是如何设置Dialog的属性。作者还分享了解决Dialog positiveButton启用问题的方法,并鼓励读者深入学习这一系列内容。
735

被折叠的 条评论
为什么被折叠?



