Подтвердить что ты не робот

HowTo использовать support.v7.preference с AppCompat и потенциальными недостатками

Я пытался реализовать настройки для приложения AppCompat, используя support.v7.preference. Мне потребовалось пару дней, чтобы обсудить это, так как support.v7.preference имеет некоторые существенные отличия от собственных предпочтений... что не так уж плохо, как только вы знаете, но, к сожалению, там мало документации. Я думал, что поделился своими выводами, чтобы другим не пришлось проходить через ту же боль.


Итак... вопрос:

Как лучше всего использовать Preferences для приложений AppCompat (с PreferenceFragment и AppCompatAcitivity, которые несовместимы)?

Вот несколько связанных вопросов:

Официальные документы здесь:

4b9b3361

Ответ 1

Решение 1: Нативный PreferenceFragment с AppCompatActivity

В AndroidStudio выберите "Файл" > "Создать проект"... > "Настройки". Этот шаблон использует обходной путь, который модифицирует нативный PreferenceFragment для работы с AppCompatActivity, похожим на support.v4.Fragment или support.v7.PreferenceFragmentCompat.

  • Pro: теперь вы можете использовать встроенную функцию Предпочтения в пределах AppCompat приложение. Это быстрый подход при использовании шаблона AS, и вы можете придерживаться существующих документов и рабочих процессов Preference.
  • Con: переоснащение не очень интуитивно понятное или чистое. Кроме того, поскольку обычно рекомендуется использовать поддерживающие библиотеки там, где это возможно, я не уверен, насколько перспективен этот подход.

Решение 2: support.v7.preference.PreferenceFragmentCompat с AppCompatActivity

  • Pro: максимизирует совместимость
  • Con: много пробелов, которые нужно преодолеть. Также это может не работать с любыми существующими расширениями-расширениями-расширениями там (например. 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'
}

Тема: Вам нужно будет определить preferenceTheme в вашем файле styles.xml, иначе запуск приложения приведет к возникновению исключения.

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
    <!-- Customize your theme here. -->
    <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

Возможно, вы захотите разделить его на разные стили для 7 +/14 +/21+. Многие люди жалуются на то, что это было ошибкой на момент написания этой статьи. Здесь есть исчерпывающий ответ .

Изменения поведения:, используя собственные настройки, очень просто: все, что вам нужно сделать, это определить/сохранить preferences.xml и использовать addPreferencesFromResource(R.xml.preferences) в вашем PreferenceFragment. Пользовательские настройки легко выполняются путем подклассификации DialogPreference, а затем просто ссылаются на preferences.xml... bam, работает.

К сожалению, у support.v7.preference было все, что связано с устранением Fragment, что избавило его от многих встроенных функций. Вместо того, чтобы просто поддерживать XML, теперь вам приходится подклассифицировать и переопределять многие вещи, все из которых, к сожалению, недокументированы.

PreferenceScreens: PreferenceScreens больше не управляются инфраструктурой. Определение PreferenceScreen в вашем preference.xml (как описано в docs) будет отображать запись, но при нажатии на нее ничего не происходит. Теперь вам решать, как отображать и перемещать суб-экраны. Скучный.

Существует один подход (описанный здесь), добавив PreferenceFragmentCompat.OnPreferenceStartScreenCallback к вашему PreferenceFragmentCompat. Хотя этот подход быстро реализуется, он просто меняет содержание существующего фрагмента предпочтений. Недостаток: нет обратной навигации, вы всегда "наверху", что не очень интуитивно для пользователя.

В другом подходе (описанном здесь) вам также понадобится управлять задним стеком, чтобы добиться обратной навигации, как ожидалось. Это использует 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));

Custom DialogPreference:. Ваши собственные предпочтения также перешли от тривиального к скучному. DialogPreference теперь имеет все, что связано с фактическим диалогом, удалено. Этот бит теперь живет в PreferenceDialogFragmentCompat. Таким образом, вам придется подклассы обоих, а затем займитесь созданием диалога и его отображением самостоятельно (пояснил здесь).

Глядя на источник PreferenceFragmentCompat.onDisplayPreferenceDialog() показывает, что он знает, как иметь дело с ровно двумя настройками диалога (EditTextPreference, ListPreference), все остальное, что вам нужно реализовать с помощью OnPreferenceDisplayDialogCallback s... кто-то задается вопросом, почему нет функции для обработки подкласса DialogPreference!


Вот некоторый код, который реализует большинство этих обходных решений и помещает их в модуль lib:

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

Основные намерения были:

  • Удалите необходимость расширения и скрипта с помощью Activity и PreferenceFragment в каждом приложении/проектах. preference.xml теперь снова является единственным файлом для каждого проекта для изменения/поддержки.
  • Обработайте и покажите PreferenceScreens (подэлементы), как ожидалось.
  • Un-split DialogPreference для восстановления собственного поведения.
  • Ручка и отображение любого подкласса DialogPreference.

Не считайте его достаточно чистым, чтобы его можно было просто использовать из коробки, но он может дать вам несколько советов при работе с подобными проблемами. Дайте ему вращение и сообщите мне, есть ли у вас какие-либо предложения.

Ответ 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:

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

Кажется, это отлично работает для меня. Очевидно, что задний стек не будет работать, но я могу жить с ним, поскольку есть кнопка "Назад".

Не идеально, но, учитывая ужасный API, я думаю, что я счастлив.

Мне бы очень хотелось услышать, если кто-то думает, что есть какие-то проблемы с этим подходом.