如何使用 android.support.v7.preference 库创建自定义首选项?
How do I create custom preferences using android.support.v7.preference library?
我希望至少支持 api 10,我希望能够很好地设置我的偏好样式,我希望能够拥有 headers(或显示 PreferenceScreen
秒)。 PreferenceActivity
似乎不完全支持 AppCompat
的着色,不适合。所以我正在尝试使用 AppCompatActivity
和 PreferenceFragmentCompat
.
public class Prefs extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null)
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new PreferencesFragment())
.commit();
}
public static class PreferencesFragment extends PreferenceFragmentCompat {
@Override public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// the following call results in a dialogue being shown
super.onDisplayPreferenceDialog(preference);
}
@Override public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
// I can probably use this to go to to a nested preference screen
// I'm not sure...
}
}
}
现在,我想创建一个提供字体选择的自定义首选项。使用 PreferenceActivity
,我可以简单地做
import android.preference.DialogPreference;
public class FontPreference extends DialogPreference {
public FontPreference(Context context, AttributeSet attrs) {super(context, attrs);}
@Override protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
// do something with builder and make a nice cute dialogue, for example, like this
builder.setSingleChoiceItems(new FontAdapter(), 0, null);
}
}
并用xml这样来显示
<my.app.FontPreference android:title="Choose font" android:summary="Unnecessary summary" />
但是现在,android.support.v7.preference.DialogPreference
里面没有onPrepareDialogBuilder
了。相反,它已移至 PreferenceDialogFragmentCompat
。我发现关于如何使用那个东西的信息很少,而且我不确定如何从 xml 到显示它。 v14 首选项片段具有以下代码:
public void onDisplayPreferenceDialog(Preference preference) {
...
final DialogFragment f;
if (preference instanceof EditTextPreference)
f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
...
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
我尝试子类化 android.support.v7.preference.DialogPreference
并让 onDisplayPreferenceDialog
使用一段类似的代码来实例化一个虚拟 FontPreferenceFragment
但它失败了,出现以下异常。
java.lang.IllegalStateException: Target fragment must implement TargetFragment interface
在这一点上,我已经陷得太深了,不想再深究了。 Google 对此异常一无所知。无论如何,这种方法似乎过于复杂。那么,使用 android.support.v7.preference 库创建自定义首选项的最佳方法是什么?
当您的 FontPreferenceFragment
没有实现 DialogPreference.TargetFragment 时会导致异常。您需要确保您的片段实现了该接口。
Important note:
Currently (v23.0.1 of the v7 library) there are still a lot of theme-issues with the 'PreferenceThemeOverlay'(see this issue). On Lollipop for example, you end up with Holo-styled category headers.
经过一些令人沮丧的时间,我终于成功创建了自定义 v7 首选项。创建自己的 Preference
似乎比您想象的要难。所以一定要花点时间。
起初您可能想知道为什么每个首选项类型都会找到 DialogPreference
和 PreferenceDialogFragmentCompat
。事实证明,第一个是实际偏好,第二个是显示偏好的 DialogFragment
。遗憾的是,您需要 subclass both[=其中 44=]。
别担心,您不需要更改任何代码。您只需要重新定位一些方法:
- 所有首选项编辑方法(如
setTitle()
或 persist*()
)都可以在 DialogPreference
class. 中找到
- 所有对话框(编辑)方法(
onBindDialogView(View)
和 onDialogClosed(boolean)
)已移至 PreferenceDialogFragmentCompat
。
您可能希望现有的 class 扩展第一个,我认为这样您就不必进行太多更改。自动完成应该可以帮助您找到缺失的方法。
完成上述步骤后,就可以将这两个 class 绑定在一起了。在您的 xml 文件中,您将引用首选项部分。但是,Android 还不知道当您的自定义首选项需要时它必须膨胀哪个 Fragment
。因此,您需要覆盖 onDisplayPreferenceDialog(Preference)
:
@Override
public void onDisplayPreferenceDialog(Preference preference) {
DialogFragment fragment;
if (preference instanceof LocationChooserDialog) {
fragment = LocationChooserFragmentCompat.newInstance(preference);
fragment.setTargetFragment(this, 0);
fragment.show(getFragmentManager(),
"android.support.v7.preference.PreferenceFragment.DIALOG");
} else super.onDisplayPreferenceDialog(preference);
}
并且您的 DialogFragment
需要处理 'key':
public static YourPreferenceDialogFragmentCompat newInstance(Preference preference) {
YourPreferenceDialogFragmentCompat fragment = new YourPreferenceDialogFragmentCompat();
Bundle bundle = new Bundle(1);
bundle.putString("key", preference.getKey());
fragment.setArguments(bundle);
return fragment;
}
这应该可以解决问题。如果您遇到问题,请尝试查看现有的子 classes 并查看 Android 如何解决它(在 Android Studio 中:键入 class' 名称并按 Ctrl+ b 查看反编译后的class)。希望对你有帮助。
有一个很好的教程和 Github 项目详细解释了如何创建扩展支持首选项库的自定义首选项 class:
https://medium.com/@JakobUlbrich/building-a-settings-screen-for-android-part-3-ae9793fd31ec -- "Building an Android Settings Screen (Part 3)",作者:Jakob Ulbrich
https://github.com/jakobulbrich/preferences-demo -- 在 Github
上的示例 Android 项目
重点是:
您将需要自定义 DialogPreference
或 ListPreference
,它控制首选项行的外观和功能。 (它还可以包含对应显示在启动对话框中的布局的引用)。将此 DialogPreference
添加到您的 XML 首选项文件中。
您将需要一个自定义 PreferenceDialogFragmentCompat
,它控制单击首选项行时对话框的启动。您可以在 onBindDialogView()
.
中配置对话框的视图
在扩展 PreferenceFragmentCompat 的首选项屏幕中,覆盖 onDisplayPreferenceDialog()
以启动自定义 PreferenceDialogFragmentCompat
.
您只能扩展支持 classes,而不是平台 classes。例如,扩展 androidx.preference.EditTextPreference
而不是 android.preference.EditTextPreference
我希望至少支持 api 10,我希望能够很好地设置我的偏好样式,我希望能够拥有 headers(或显示 PreferenceScreen
秒)。 PreferenceActivity
似乎不完全支持 AppCompat
的着色,不适合。所以我正在尝试使用 AppCompatActivity
和 PreferenceFragmentCompat
.
public class Prefs extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null)
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new PreferencesFragment())
.commit();
}
public static class PreferencesFragment extends PreferenceFragmentCompat {
@Override public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// the following call results in a dialogue being shown
super.onDisplayPreferenceDialog(preference);
}
@Override public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
// I can probably use this to go to to a nested preference screen
// I'm not sure...
}
}
}
现在,我想创建一个提供字体选择的自定义首选项。使用 PreferenceActivity
,我可以简单地做
import android.preference.DialogPreference;
public class FontPreference extends DialogPreference {
public FontPreference(Context context, AttributeSet attrs) {super(context, attrs);}
@Override protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
// do something with builder and make a nice cute dialogue, for example, like this
builder.setSingleChoiceItems(new FontAdapter(), 0, null);
}
}
并用xml这样来显示
<my.app.FontPreference android:title="Choose font" android:summary="Unnecessary summary" />
但是现在,android.support.v7.preference.DialogPreference
里面没有onPrepareDialogBuilder
了。相反,它已移至 PreferenceDialogFragmentCompat
。我发现关于如何使用那个东西的信息很少,而且我不确定如何从 xml 到显示它。 v14 首选项片段具有以下代码:
public void onDisplayPreferenceDialog(Preference preference) {
...
final DialogFragment f;
if (preference instanceof EditTextPreference)
f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
...
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
我尝试子类化 android.support.v7.preference.DialogPreference
并让 onDisplayPreferenceDialog
使用一段类似的代码来实例化一个虚拟 FontPreferenceFragment
但它失败了,出现以下异常。
java.lang.IllegalStateException: Target fragment must implement TargetFragment interface
在这一点上,我已经陷得太深了,不想再深究了。 Google 对此异常一无所知。无论如何,这种方法似乎过于复杂。那么,使用 android.support.v7.preference 库创建自定义首选项的最佳方法是什么?
当您的 FontPreferenceFragment
没有实现 DialogPreference.TargetFragment 时会导致异常。您需要确保您的片段实现了该接口。
Important note: Currently (v23.0.1 of the v7 library) there are still a lot of theme-issues with the 'PreferenceThemeOverlay'(see this issue). On Lollipop for example, you end up with Holo-styled category headers.
经过一些令人沮丧的时间,我终于成功创建了自定义 v7 首选项。创建自己的 Preference
似乎比您想象的要难。所以一定要花点时间。
起初您可能想知道为什么每个首选项类型都会找到 DialogPreference
和 PreferenceDialogFragmentCompat
。事实证明,第一个是实际偏好,第二个是显示偏好的 DialogFragment
。遗憾的是,您需要 subclass both[=其中 44=]。
别担心,您不需要更改任何代码。您只需要重新定位一些方法:
- 所有首选项编辑方法(如
setTitle()
或persist*()
)都可以在DialogPreference
class. 中找到
- 所有对话框(编辑)方法(
onBindDialogView(View)
和onDialogClosed(boolean)
)已移至PreferenceDialogFragmentCompat
。
您可能希望现有的 class 扩展第一个,我认为这样您就不必进行太多更改。自动完成应该可以帮助您找到缺失的方法。
完成上述步骤后,就可以将这两个 class 绑定在一起了。在您的 xml 文件中,您将引用首选项部分。但是,Android 还不知道当您的自定义首选项需要时它必须膨胀哪个 Fragment
。因此,您需要覆盖 onDisplayPreferenceDialog(Preference)
:
@Override
public void onDisplayPreferenceDialog(Preference preference) {
DialogFragment fragment;
if (preference instanceof LocationChooserDialog) {
fragment = LocationChooserFragmentCompat.newInstance(preference);
fragment.setTargetFragment(this, 0);
fragment.show(getFragmentManager(),
"android.support.v7.preference.PreferenceFragment.DIALOG");
} else super.onDisplayPreferenceDialog(preference);
}
并且您的 DialogFragment
需要处理 'key':
public static YourPreferenceDialogFragmentCompat newInstance(Preference preference) {
YourPreferenceDialogFragmentCompat fragment = new YourPreferenceDialogFragmentCompat();
Bundle bundle = new Bundle(1);
bundle.putString("key", preference.getKey());
fragment.setArguments(bundle);
return fragment;
}
这应该可以解决问题。如果您遇到问题,请尝试查看现有的子 classes 并查看 Android 如何解决它(在 Android Studio 中:键入 class' 名称并按 Ctrl+ b 查看反编译后的class)。希望对你有帮助。
有一个很好的教程和 Github 项目详细解释了如何创建扩展支持首选项库的自定义首选项 class:
https://medium.com/@JakobUlbrich/building-a-settings-screen-for-android-part-3-ae9793fd31ec -- "Building an Android Settings Screen (Part 3)",作者:Jakob Ulbrich
https://github.com/jakobulbrich/preferences-demo -- 在 Github
上的示例 Android 项目
重点是:
您将需要自定义
DialogPreference
或ListPreference
,它控制首选项行的外观和功能。 (它还可以包含对应显示在启动对话框中的布局的引用)。将此DialogPreference
添加到您的 XML 首选项文件中。您将需要一个自定义
PreferenceDialogFragmentCompat
,它控制单击首选项行时对话框的启动。您可以在onBindDialogView()
. 中配置对话框的视图
在扩展 PreferenceFragmentCompat 的首选项屏幕中,覆盖
onDisplayPreferenceDialog()
以启动自定义PreferenceDialogFragmentCompat
.您只能扩展支持 classes,而不是平台 classes。例如,扩展
androidx.preference.EditTextPreference
而不是android.preference.EditTextPreference