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

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


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

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

Вот пара связанных вопросов:

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

2 ответа

Решение

Решение 1: родной PreferenceFragment с AppCompatActivity

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

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

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

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

Тема: Вам нужно определить 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... БАМ, работает.

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

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

Существует один подход (описанный здесь), добавление 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() показывает, что знает, как обращаться с 2-мя диалоговыми настройками (EditTextPreference, ListPreference), все остальное, что вам придется реализовать самостоятельно, используя OnPreferenceDisplayDialogCallback s... каждый задается вопросом, почему нет функциональности для обработки подкласса DialogPreference!


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

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

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

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

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

У меня есть альтернативное решение для этого, я хотел бы получить обратную связь.

Я сделал пользовательский макет для своего предпочтительного фрагмента с кнопкой "Назад" в верхнем левом углу.

Во-первых, в "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 я думаю, что я счастлив.

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

Другие вопросы по тегам