Как использовать support.v7.preference с AppCompat и потенциальные недостатки
Я пытался реализовать настройки для приложения AppCompat, используя support.v7.preference. Мне потребовалось несколько дней, чтобы разобраться с этим, так как support.v7.preference имеет некоторые существенные отличия от нативных предпочтений... что не так уж плохо, как вы знаете, но, к сожалению, документации там немного. Я думал, что поделюсь своими выводами, чтобы другим не пришлось переживать такую же боль.
Итак... вопрос:
Как наилучшим образом реализовать настройки для приложений AppCompat (с несовместимостью PreferenceFragment и AppCompatAcitivity)?
Вот пара связанных вопросов:
- Подэкран настроек не открывается при использовании support.v7.preference
- Как вернуться с подэкрана "Установки" на главный экран в PreferenceFragmentCompat?
- PreferenceFragmentCompat требует установки preferenceTheme
- Как создать пользовательские настройки с помощью библиотеки android.support.v7.preference?
Официальные документы здесь:
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 я думаю, что я счастлив.
Я хотел бы услышать, если кто-то думает, что есть какие-то проблемы с этим подходом.