【问题标题】:HowTo use support.v7.preference with AppCompat and potential drawbacks如何将 support.v7.preference 与 AppCompat 和潜在缺点一起使用
【发布时间】:2017-11-02 17:16:15
【问题描述】:

我正在尝试使用 support.v7.preference 为 AppCompat 应用程序实现首选项。我花了几天的时间来整理它,因为 support.v7.preference 与本机偏好有一些显着差异......一旦你知道这还不错,但不幸的是那里几乎没有文档。我想我会分享我的发现,这样其他人就不必经历同样的痛苦。


所以...问题:

您如何最好地为 AppCompat 应用实现 Preferences(PreferenceFragment 和 AppCompatAcitivity 不兼容)?

这里有几个相关的问题:

官方文档在这里:

【问题讨论】:

    标签: android android-appcompat android-preferences preferencescreen preference-v7


    【解决方案1】:

    解决方案 1:原生 PreferenceFragmentAppCompatActivity

    在 AndroidStudio 中,选择 File > New Project >...> SettingsActivity。此模板使用一种解决方法,将原生 PreferenceFragment 改造为与 AppCompatActivity 一起使用,类似于 support.v4.Fragmentsupport.v7.PreferenceFragmentCompat

    • 专业版:您现在可以在 AppCompat 应用程序。这是使用 AS 模板时的快速方法,您可以坚持现有的 Preference 文档和工作流程。
    • 缺点:改造不是很直观或干净。此外,由于通常建议在可用的情况下使用支持库,因此我不确定这种方法是否具有前瞻性。

    解决方案 2:support.v7.preference.PreferenceFragmentCompatAppCompatActivity

    • 专业版:最大限度地提高兼容性
    • 缺点:有很多差距需要弥补。此外,这可能不适用于任何现有的偏好扩展库(例如 ColorPickerFontPreferences)。

    如果您选择不使用解决方案 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+ 的不同样式。在撰写本文时,很多人抱怨这是错误的。 here 有一个非常全面的答案。

    行为变化:使用本机首选项非常简单:您只需定义/维护您的preferences.xml 并在您的PreferenceFragment 中使用addPreferencesFromResource(R.xml.preferences)。自定义首选项很容易通过对DialogPreference 进行子类化来完成,然后只需在preferences.xml 中引用... bam,就可以了。

    不幸的是,support.v7.preference 已经剥离了与处理Fragment 相关的所有内容,使其失去了很多内置功能。您现在不仅要维护 XML,还必须继承和覆盖很多东西,不幸的是,所有这些都没有记录。

    PreferenceScreens: PreferenceScreens 不再由框架管理。在preference.xml 中定义PreferenceScreen(如docs 中所述)将显示该条目,但单击它不会执行任何操作。现在由您来处理显示和导航子屏幕。无聊。

    有一种方法(描述为here),将PreferenceFragmentCompat.OnPreferenceStartScreenCallback 添加到您的PreferenceFragmentCompat。虽然这种方法可以快速实施,但它只是交换了现有偏好片段的内容。缺点是:没有返回导航,你总是“在顶部”,这对用户来说不是很直观。

    在另一种方法中(描述为here),您还必须管理后退堆栈以实现预期的后退导航。这使用preferenceScreen.getKey() 作为每个新创建/显示的片段的根。

    这样做时,您可能还会偶然发现 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));
    

    自定义 DialogPreference: 制定自己的偏好也从琐碎变得无聊。 DialogPreference 现在删除了处理实际对话框的所有内容。该位现在位于PreferenceDialogFragmentCompat。所以你必须对两者进行子类化,然后自己创建对话框并显示它(解释为here)。

    查看PreferenceFragmentCompat.onDisplayPreferenceDialog() 的源代码表明它知道如何处理恰好2 个对话框首选项(EditTextPreferenceListPreference),其他一切您必须使用OnPreferenceDisplayDialogCallbacks 自己实现...有人想知道,为什么没有处理DialogPreference 的子类的功能!


    这里有一些代码实现了大多数这些变通方法并将它们装箱到一个 lib 模块中:

    https://github.com/mstummer/extended-preferences-compat.git

    主要意图是:

    • 无需在每个应用程序/项目中扩展和摆弄ActivityPreferenceFragmentpreference.xml 现在再次成为每个项目中唯一需要更改/维护的文件。
    • 按预期处理和显示PreferenceScreens(子屏幕)。
    • 取消拆分 DialogPreference 以恢复本机行为。
    • 处理和显示DialogPreference 的任何子类。

    不要认为开箱即用就足够干净,但在处理类似问题时它可能会给您一些提示。试一试,如果您有任何建议,请告诉我。

    【讨论】:

    • 你是救生员 Max @maxdownunder!!
    • 这是否适用于子屏幕?似乎没有任何效果。
    • 知道了。我在“PrefrenceFragmentCompat::onCreatePreferences(..)”中有“addPreferencesFromResource(R.xml.settings)”而不是“我有“setPreferencesFromResource(R.xml.settings,rootKey)”
    • 我不知道compile 'com.android.support:support-v4:23.1.1' 甚至是一个选项,感谢您指出这一点并让我免于头痛!
    • 所以谷歌已经有效地弃用了这整个功能。谁有时间搞这一切?滚动您自己的 UI 并保存到共享首选项可能会更快。
    【解决方案2】:

    我有一个替代解决方案,我希望得到反馈。

    我为我的偏好片段做了一个自定义布局,左上角有一个“返回”按钮。

    首先,在“onCreatePreference”中,我存储了根 PreferenceScreen:

    root = this.getPreferenceScreen();
    

    然后,我如上所述在其他线程中添加 OnPreferenceStartScreenCallback 以使片段转到子屏幕,但在我的“onPreferenceStartScreen”中,我还将后退按钮设置为可见,如下所示:

        public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
            preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
            backButton.setVisibility(View.VISIBLE);
            return true;
    }
    

    最后是backButton clickhandler:

        setPreferenceScreen(root);
        back.setVisibility(View.GONE);
    

    这对我来说似乎很好用。显然后退堆栈不起作用,但我可以忍受,因为有一个后退按钮。

    不完美,但考虑到糟糕的 API,我觉得我很高兴。

    如果有人认为这种方法有任何问题,我很想听听。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多