如何使用 support.v7.preference 与 AppCompat 和潜在的缺点
HowTo use support.v7.preference with AppCompat and potential drawbacks
我正在尝试使用支持实现 AppCompat 应用程序的首选项。v7.preference。我花了几天时间 fiddle 通过它,因为 support.v7.preference 与本地首选项有一些显着差异......一旦你知道这并不算太糟糕,但不幸的是几乎没有文档在那里。我想我会分享我的发现,这样其他人就不必经历同样的痛苦。
所以...问题:
如何最好地实现 AppCompat 应用程序的首选项(PreferenceFragment 和 AppCompatAcitivity 不兼容)?
这里有几个相关的问题:
官方文档在这里:
解决方案 1:本机 PreferenceFragment
与 AppCompatActivity
在 AndroidStudio 中,选择 文件 > 新建项目 >...> SettingsActivity。此模板使用一种变通方法来改造原生 PreferenceFragment
以与 AppCompatActivity
一起使用,类似于 support.v4.Fragment
或 support.v7.PreferenceFragmentCompat
.
- 专业版:您现在可以在
AppCompat
应用程序。这是使用 AS 模板时的快速方法,您可以坚持现有的首选项文档和工作流程。
- 缺点:改装不是很直观或干净。此外,由于通常建议在可用的情况下使用支持库,我不确定 future-proof 这种方法如何。
解决方案 2:support.v7.preference.PreferenceFragmentCompat
与 AppCompatActivity
- 专业版:最大化兼容性
- 缺点:需要弥合的差距很多。此外,这可能不适用于任何现有的 preference-extensions-libs(例如
ColorPicker
或 FontPreferences
)。
如果您选择不使用解决方案 1(我仍然不确定两者中哪一个更适合未来),使用 support.v7.preference
时有几个缺点。
下面提到了使用解决方案 2 的重要缺点。
依赖关系:
dependencies {
...
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:preference-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
}
主题:
您需要在 styles.xml 中定义 preferenceTheme
,否则 运行 您的应用将引发异常。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
您可能想将其分成不同的样式以供 7+/14+/21+。在撰写本文时,很多人抱怨这是错误的。有一个非常全面的答案 .
行为更改: 使用本机首选项非常简单:您需要做的就是 define/maintain 您的 preferences.xml
并使用 addPreferencesFromResource(R.xml.preferences)
在你的 PreferenceFragment
中。 sub-classing DialogPreference
可以轻松完成自定义首选项,然后只需在 preferences.xml
中引用... bam,有效。
不幸的是,support.v7.preference
已经删除了与处理 Fragment
相关的所有内容,使其失去了很多 built-in 的功能。您现在必须 sub-class 并覆盖很多东西,而不是仅仅维护 XML,不幸的是,所有这些都没有记录。
PreferenceScreens: PreferenceScreens
不再由框架管理。在 preference.xml
中定义 PreferenceScreen
(如 docs 中所述)将显示该条目,但单击它不会执行任何操作。现在由您来处理显示和导航 sub-screens。无聊。
有一种方法(描述为 ),在您的 PreferenceFragmentCompat
中添加一个 PreferenceFragmentCompat.OnPreferenceStartScreenCallback
。虽然这种方法实施很快,但它只是交换现有偏好片段的内容。缺点是:没有后退导航,你总是'at the top',这对用户来说不是很直观。
在另一种方法(描述为 )中,您还必须管理返回堆栈,以便按预期实现返回导航。这使用 preferenceScreen.getKey()
作为每个新 created/displayed 片段的根。
这样做时,您可能还会偶然发现 PreferenceFragments
默认情况下是透明的并且奇怪地叠加在一起。人们倾向于覆盖 PreferenceFragmentCompat.onViewCreated()
以添加类似
的内容
// Set the default white background in the view so as to avoid transparency
view.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.background_material_light));
Custom DialogPreference: 制作自己的首选项也从琐碎变得无聊。 DialogPreference
现在删除了处理实际对话框的所有内容。该位现在位于 PreferenceDialogFragmentCompat
中。所以你必须 sub-class 两者,然后处理创建对话框并自己显示它(解释 )。
查看 PreferenceFragmentCompat.onDisplayPreferenceDialog()
的源代码表明它知道如何处理恰好 2 个对话框首选项(EditTextPreference
、ListPreference
),其他一切你必须自己实现使用 OnPreferenceDisplayDialogCallback
s...有人想知道,为什么没有处理 DialogPreference
的 sub-class 的功能!
下面是一些实现这些解决方法中的大部分的代码,并将它们封装在一个 lib 模块中:
https://github.com/mstummer/extended-preferences-compat.git
主要意图是:
- 删除在每个 app/projects 中使用
Activity
和 PreferenceFragment
扩展和 fiddle 的需要。 preference.xml
现在再次成为 change/maintain 的唯一 per-project 文件。
- 按预期处理和显示
PreferenceScreens
(sub-screens)。
- Un-split
DialogPreference
恢复本机行为。
- 处理并显示
DialogPreference
中的任意 sub-class。
不要认为它足够干净,可以开箱即用,但在处理类似问题时它可能会给你一些提示。试一试,如果您有任何建议,请告诉我。
我有一个替代解决方案,我希望得到反馈。
我为我的 preferencefragment 做了一个自定义布局,在左上角有一个 "Back" 按钮。
首先,在 "onCreatePreference" 我存储了根 PreferenceScreen:
root = this.getPreferenceScreen();
然后,我如上所述添加 OnPreferenceStartScreenCallback 并在其他线程中添加该片段以使该片段进入子屏幕,但在我的 "onPreferenceStartScreen" 中我还将后退按钮设置为可见,如下所示:
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
backButton.setVisibility(View.VISIBLE);
return true;
}
最后,后退按钮点击处理程序:
setPreferenceScreen(root);
back.setVisibility(View.GONE);
这对我来说似乎很好用。显然后退堆栈不起作用,但我可以接受,因为有一个后退按钮。
不完美,但鉴于糟糕API我觉得我很开心。
如果有人认为这种方法有任何问题,我很想听听。
我正在尝试使用支持实现 AppCompat 应用程序的首选项。v7.preference。我花了几天时间 fiddle 通过它,因为 support.v7.preference 与本地首选项有一些显着差异......一旦你知道这并不算太糟糕,但不幸的是几乎没有文档在那里。我想我会分享我的发现,这样其他人就不必经历同样的痛苦。
所以...问题:
如何最好地实现 AppCompat 应用程序的首选项(PreferenceFragment 和 AppCompatAcitivity 不兼容)?
这里有几个相关的问题:
官方文档在这里:
解决方案 1:本机 PreferenceFragment
与 AppCompatActivity
在 AndroidStudio 中,选择 文件 > 新建项目 >...> SettingsActivity。此模板使用一种变通方法来改造原生 PreferenceFragment
以与 AppCompatActivity
一起使用,类似于 support.v4.Fragment
或 support.v7.PreferenceFragmentCompat
.
- 专业版:您现在可以在
AppCompat
应用程序。这是使用 AS 模板时的快速方法,您可以坚持现有的首选项文档和工作流程。 - 缺点:改装不是很直观或干净。此外,由于通常建议在可用的情况下使用支持库,我不确定 future-proof 这种方法如何。
解决方案 2:support.v7.preference.PreferenceFragmentCompat
与 AppCompatActivity
- 专业版:最大化兼容性
- 缺点:需要弥合的差距很多。此外,这可能不适用于任何现有的 preference-extensions-libs(例如
ColorPicker
或FontPreferences
)。
如果您选择不使用解决方案 1(我仍然不确定两者中哪一个更适合未来),使用 support.v7.preference
时有几个缺点。
下面提到了使用解决方案 2 的重要缺点。
依赖关系:
dependencies {
...
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:preference-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
}
主题:
您需要在 styles.xml 中定义 preferenceTheme
,否则 运行 您的应用将引发异常。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
您可能想将其分成不同的样式以供 7+/14+/21+。在撰写本文时,很多人抱怨这是错误的。有一个非常全面的答案
行为更改: 使用本机首选项非常简单:您需要做的就是 define/maintain 您的 preferences.xml
并使用 addPreferencesFromResource(R.xml.preferences)
在你的 PreferenceFragment
中。 sub-classing DialogPreference
可以轻松完成自定义首选项,然后只需在 preferences.xml
中引用... bam,有效。
不幸的是,support.v7.preference
已经删除了与处理 Fragment
相关的所有内容,使其失去了很多 built-in 的功能。您现在必须 sub-class 并覆盖很多东西,而不是仅仅维护 XML,不幸的是,所有这些都没有记录。
PreferenceScreens: PreferenceScreens
不再由框架管理。在 preference.xml
中定义 PreferenceScreen
(如 docs 中所述)将显示该条目,但单击它不会执行任何操作。现在由您来处理显示和导航 sub-screens。无聊。
有一种方法(描述为 PreferenceFragmentCompat
中添加一个 PreferenceFragmentCompat.OnPreferenceStartScreenCallback
。虽然这种方法实施很快,但它只是交换现有偏好片段的内容。缺点是:没有后退导航,你总是'at the top',这对用户来说不是很直观。
在另一种方法(描述为 preferenceScreen.getKey()
作为每个新 created/displayed 片段的根。
这样做时,您可能还会偶然发现 PreferenceFragments
默认情况下是透明的并且奇怪地叠加在一起。人们倾向于覆盖 PreferenceFragmentCompat.onViewCreated()
以添加类似
// Set the default white background in the view so as to avoid transparency
view.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.background_material_light));
Custom DialogPreference: 制作自己的首选项也从琐碎变得无聊。 DialogPreference
现在删除了处理实际对话框的所有内容。该位现在位于 PreferenceDialogFragmentCompat
中。所以你必须 sub-class 两者,然后处理创建对话框并自己显示它(解释
查看 PreferenceFragmentCompat.onDisplayPreferenceDialog()
的源代码表明它知道如何处理恰好 2 个对话框首选项(EditTextPreference
、ListPreference
),其他一切你必须自己实现使用 OnPreferenceDisplayDialogCallback
s...有人想知道,为什么没有处理 DialogPreference
的 sub-class 的功能!
下面是一些实现这些解决方法中的大部分的代码,并将它们封装在一个 lib 模块中:
https://github.com/mstummer/extended-preferences-compat.git
主要意图是:
- 删除在每个 app/projects 中使用
Activity
和PreferenceFragment
扩展和 fiddle 的需要。preference.xml
现在再次成为 change/maintain 的唯一 per-project 文件。 - 按预期处理和显示
PreferenceScreens
(sub-screens)。 - Un-split
DialogPreference
恢复本机行为。 - 处理并显示
DialogPreference
中的任意 sub-class。
不要认为它足够干净,可以开箱即用,但在处理类似问题时它可能会给你一些提示。试一试,如果您有任何建议,请告诉我。
我有一个替代解决方案,我希望得到反馈。
我为我的 preferencefragment 做了一个自定义布局,在左上角有一个 "Back" 按钮。
首先,在 "onCreatePreference" 我存储了根 PreferenceScreen:
root = this.getPreferenceScreen();
然后,我如上所述添加 OnPreferenceStartScreenCallback 并在其他线程中添加该片段以使该片段进入子屏幕,但在我的 "onPreferenceStartScreen" 中我还将后退按钮设置为可见,如下所示:
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
backButton.setVisibility(View.VISIBLE);
return true;
}
最后,后退按钮点击处理程序:
setPreferenceScreen(root);
back.setVisibility(View.GONE);
这对我来说似乎很好用。显然后退堆栈不起作用,但我可以接受,因为有一个后退按钮。
不完美,但鉴于糟糕API我觉得我很开心。
如果有人认为这种方法有任何问题,我很想听听。