Информационная архитектура

В Android 8.0 была добавлена новая информационная архитектура приложения "Настройки", чтобы упростить организацию настроек и помочь пользователям быстрее находить нужные параметры для настройки устройств Android. В Android 9 были внесены улучшения, которые расширили функциональность настроек и упростили их реализацию.

Примеры и источник

Большинство страниц в настройках уже реализованы с использованием нового фреймворка. Хороший пример – DisplaySettings: packages/apps/Settings/src/com/android/settings/DisplaySettings.java

Ниже приведены пути к файлам важных компонентов.

  • CategoryKey: packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
  • DashboardFragmentRegistry: packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
  • DashboardFragment: packages/apps/Settings/src/com/android/settings/dashboard/DashboardFragment.java
  • AbstractPreferenceController: frameworks/base/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
  • BasePreferenceController (появился в Android 9): packages/apps/Settings/src/com/android/settings/core/BasePreferenceController.java

Реализация

Производителям устройств рекомендуется адаптировать существующую архитектуру информации о настройках и при необходимости добавлять страницы с настройками для функций партнеров. Перенос настроек со старой страницы (реализованной как SettingsPreferencePage) на новую (реализованную с помощью DashboardFragment) может быть сложным. Скорее всего, настройки со старой страницы не реализованы с помощью PreferenceController.

Поэтому при переносе настроек со старой страницы на новую необходимо создать PreferenceController и переместить код в контроллер, прежде чем создавать его экземпляр в новом DashboardFragment. API, необходимые для работы PreferenceController, описаны в их названиях и документации Javadoc.

Мы настоятельно рекомендуем добавить модульное тестирование для каждого тега PreferenceController. Если изменение отправлено в AOSP, требуется модульное тестирование. Чтобы узнать больше о том, как писать тесты на основе Robolectric, ознакомьтесь с файлом readme packages/apps/Settings/tests/robotests/README.md.

Информационная архитектура в стиле плагина

Каждый элемент настроек реализован как параметр. Настройки можно легко переносить с одной страницы на другую.

Чтобы упростить перемещение настроек, в Android 8.0 был добавлен фрагмент хоста в виде плагина, содержащий элементы настроек. Элементы настроек представлены в виде контроллеров, похожих на плагины. Таким образом, страница настроек состоит из одного фрагмента хоста и нескольких контроллеров настроек.

DashboardFragment

DashboardFragment – это хост для контроллеров настроек в виде плагинов. Фрагмент наследуется от PreferenceFragment и имеет хуки для расширения и обновления как статических, так и динамических списков настроек.

Статические настройки

Статический список предпочтений определяется в XML с помощью тега <Preference>. При реализации DashboardFragment используется метод getPreferenceScreenResId(), чтобы указать, в каком XML-файле содержится статический список предпочтений, которые нужно показывать.

Динамические настройки

Динамический элемент представляет собой карточку с интентом, ведущую к внешней или внутренней Activity. Обычно намерение ведет на другую страницу настроек. Например, элемент "Google" на главной странице настроек является динамическим. Динамические объекты определяются в файле AndroidManifest (описан ниже) и загружаются через файл FeatureProvider (определяется как DashboardFeatureProvider).

Динамические настройки более ресурсоемкие, чем статические, поэтому обычно разработчики используют статические настройки. Однако динамические настройки могут быть полезны в следующих случаях:

  • Настройка не реализована непосредственно в приложении "Настройки" (например, не внедрена настройка, реализованная в приложениях OEM/оператора).
  • Настройка должна появиться на главной странице настроек.
  • У вас уже есть Activity для настройки, и вы не хотите реализовывать дополнительную статическую конфигурацию.

Чтобы настроить действие как динамический параметр, выполните следующие действия:

  • Отметьте действие как динамическое, добавив в него фильтр intent.
  • Укажите категорию для приложения "Настройки". Категория – это константа, определенная в CategoryKey.
  • При необходимости добавьте сводный текст, который будет показываться при отображении настройки.

Вот пример из приложения "Настройки" для DisplaySettings.

<activity android:name="Settings$DisplaySettingsActivity"
                   android:label="@string/display_settings"
                   android:icon="@drawable/ic_settings_display">
             <!-- Mark the activity as a dynamic setting -->
              <intent-filter>
                     <action android:name="com.android.settings.action.IA_SETTINGS" />
              </intent-filter>
             <!-- Tell Settings app which category it belongs to -->
              <meta-data android:name="com.android.settings.category"
                     android:value="com.android.settings.category.ia.homepage" />
             <!-- Add a summary text when the setting is displayed -->
              <meta-data android:name="com.android.settings.summary"
                     android:resource="@string/display_dashboard_summary"/>
             </activity>

Во время отрисовки фрагмент запросит список настроек из статического XML-файла и динамических настроек, заданных в AndroidManifest. Независимо от того, где определены объекты PreferenceController – в коде Java или в XML, – DashboardFragment управляет логикой обработки каждого параметра с помощью PreferenceController (подробнее об этом ниже). После этого в интерфейсе будет показан смешанный список.

PreferenceController

В этом разделе описаны различия между реализацией PreferenceController в Android 9 и Android 8.x.

PreferenceController в Android 9

PreferenceController содержит всю логику для взаимодействия с предпочтениями, включая отображение, обновление, индексацию для поиска и т. д.

Интерфейс PreferenceController определяется как BasePreferenceController. Например, код в packages/apps/Settings/src/com/android/settings/core/ BasePreferenceController.java

У класса BasePreferenceController есть несколько подклассов, каждый из которых соответствует определенному стилю интерфейса, поддерживаемому приложением "Настройки" по умолчанию. Например, у TogglePreferenceController есть API, который напрямую сопоставляется с тем, как пользователь должен взаимодействовать с интерфейсом на основе переключателей.

BasePreferenceController имеет такие API, как getAvailabilityStatus(), displayPreference(), handlePreferenceTreeClicked(), и т. д. Подробная документация по каждому API приведена в классе интерфейса.

Ограничение на реализацию BasePreferenceController (и его подклассов, например TogglePreferenceController) заключается в том, что сигнатура конструктора должна соответствовать одному из следующих вариантов:

  • public MyController(Context context, String key) {}
  • public MyController(Context context) {}

При установке настройки для фрагмента панель управления предоставляет метод для прикрепления PreferenceController перед показом. При установке контроллер подключается к фрагменту, чтобы все будущие релевантные события отправлялись контроллеру.

DashboardFragment ведет список объектов PreferenceController на экране. В методе onCreate() фрагмента вызываются все контроллеры для метода getAvailabilityStatus(). Если он возвращает значение true, вызывается метод displayPreference() для обработки логики показа. getAvailabilityStatus() также важно, чтобы фреймворк настроек знал, какие элементы доступны при поиске.

PreferenceController в версиях Android 8.x

PreferenceController содержит всю логику взаимодействия с настройками, включая отображение, обновление, индексацию в поиске и т. д.

Для взаимодействия с настройками в интерфейсе PreferenceController есть API isAvailable(), displayPreference(), handlePreferenceTreeClicked() и т. д. Подробную документацию по каждому API можно найти в классе интерфейса.

При установке настройки для фрагмента панель управления предоставляет метод для прикрепления PreferenceController перед показом. Во время установки контроллер подключается к фрагменту, чтобы все будущие события отправлялись ему.

DashboardFragment хранит список PreferenceControllers на экране. В методе onCreate() фрагмента вызываются все контроллеры для метода isAvailable(). Если он возвращает значение true, вызывается метод displayPreference() для обработки логики показа.

Как использовать DashboardFragment

Как перенести предпочтение со страницы А на страницу Б

Если параметр статически указан в исходном XML-файле параметров страницы, следуйте процедуре переноса Static для вашей версии Android, приведенной ниже. В противном случае следуйте инструкциям по динамическому переносу для вашей версии Android.

Статическое перемещение в Android 9

  1. Найдите XML-файлы настроек для исходной и целевой страниц. Эту информацию можно найти с помощью метода getPreferenceScreenResId() страницы.
  2. Удалите параметр из XML-кода исходной страницы.
  3. Добавьте предпочтение в XML-код целевой страницы.
  4. Удалите PreferenceController для этого параметра из реализации Java на исходной странице. Обычно это происходит в течение createPreferenceControllers(). Контроллер может быть объявлен непосредственно в XML.

    Примечание. У предпочтения может не быть PreferenceController.

  5. Создайте экземпляр PreferenceController в теге createPreferenceControllers() на целевой странице. Если тег PreferenceController определен в XML-файле старой страницы, его нужно определить в XML-файле новой страницы.

Динамическое перемещение в Android 9

  1. Определите, к какой категории относятся исходная и целевая страницы. Эту информацию можно найти в DashboardFragmentRegistry.
  2. Откройте файл AndroidManifest.xml, в котором находится нужная настройка, и найдите запись Activity, представляющую эту настройку.
  3. Установите для значения метаданных действия com.android.settings.category ключ категории новой страницы.

Статический перенос в Android 8.x

  1. Найдите XML-файлы настроек для исходной и целевой страниц.
  2. Эту информацию можно найти с помощью метода getPreferenceScreenResId() .
  3. Удалите настройку из XML-файла исходной страницы.
  4. Добавьте предпочтение в XML целевой страницы.
  5. Удалите PreferenceController для этого параметра в реализации Java на исходной странице. Обычно это getPreferenceControllers().
  6. Примечание. Возможно, у предпочтения нет PreferenceController.

  7. Создайте экземпляр PreferenceController в getPreferenceControllers() целевой страницы.

Динамическое перемещение в Android 8.x

  1. Определите, к какой категории относятся исходная и целевая страницы. Эту информацию можно найти в DashboardFragmentRegistry.
  2. Откройте файл AndroidManifest.xml, в котором находится нужная настройка, и найдите запись Activity, представляющую эту настройку.
  3. Измените значение метаданных действия для com.android.settings.category, указав ключ категории новой страницы.

Как создать новый параметр на странице

Если параметр статически указан в исходном XML-файле параметров страницы, выполните статическую процедуру ниже. В противном случае следуйте динамической процедуре.

Как создать статическую настройку

  1. Найдите XML-файлы настроек для страницы. Эту информацию можно найти с помощью метода getPreferenceScreenResId() на странице.
  2. Добавьте в XML новый элемент Preference. Убедитесь, что у него уникальное значение атрибута "идентификатор" android:key.
  3. Определите PreferenceController для этого параметра в методе getPreferenceControllers() страницы.
    • В Android 8.x и, при необходимости, в Android 9 создайте экземпляр PreferenceController для этого параметра в методе createPreferenceControllers() страницы.

      Если этот параметр уже был задан в других местах, возможно, для него уже существует PreferenceController. Вы можете использовать PreferenceController повторно, не создавая новый.

    • В Android 9 и более поздних версиях можно объявлять PreferenceController в XML рядом с настройкой. Пример:
      <Preference
              android:key="reset_dashboard"
              android:title="@string/reset_dashboard_title"
              settings:controller="com.android.settings.system.ResetPreferenceController"/>

Как создать динамическую настройку

  1. Определите, к какой категории относится исходная и целевая страница. Эту информацию можно найти в DashboardFragmentRegistry.
  2. Как создать объект Activity в приложении "AndroidManifest"
  3. Добавьте в новый объект Activity необходимые метаданные, чтобы определить настройки. Задайте для метаданных com.android.settings.category то же значение, что и на шаге 1.

Как создать новую страницу

  1. Создайте новый фрагмент, наследуя его от DashboardFragment.
  2. Укажите категорию в DashboardFragmentRegistry.

    Примечание. Этот шаг выполнять необязательно. Если на этой странице вам не нужны динамические настройки, указывать ключ категории не требуется.

  3. Следуйте инструкциям по добавлению настроек, необходимых для этой страницы. Подробнее о реализации…

Проверка

  • Запустите тесты Robolectric в настройках. Все существующие и новые тесты должны пройти.
  • Создайте и установите настройки, а затем вручную откройте страницу, которую нужно изменить. Страница должна обновиться сразу.