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

Применение темы AppCompat к индивидуальным предпочтениям в PreferenceFragment

Я боролся, пытаясь заставить мою PreferenceFragment иметь ту же материальную тему и стиль (через AppCompat), что и остальная часть моего приложения. PreferenceFragment, который я использую для управления всеми настройками моего приложения, показан ниже:

enter image description here

Как вы можете видеть на скриншоте выше, я смог настроить PreferenceFragment с помощью colorAccent, colorPrimary и нескольких других атрибутов. Моя тема для PreferenceFragment выглядит следующим образом:

<style
    name="settingsTheme"
    parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/blue_grey_500</item>
    <item name="colorAccent">@color/blue_grey_500</item>
    <item name="android:textColor">@color/black_text_alt</item>
    <item name="android:textColorSecondary">@color/black_secondary_text</item>
</style>

Однако, несмотря на все мои усилия, я не могу применять темы к отдельным элементам в моем PreferenceFragment, например ListPreference и EditTextPreference; все предпочтения по-прежнему сохраняют стандартную тему AppCompat:

enter image description here

Я нашел 2-летний пост, который обсуждает эту проблему, хотя и без реального решения: Как применить тему к <PreferenceScreen> элементы <PreferenceCategory>

Мне интересно, смог ли кто-нибудь успешно применить темы к предпочтениям, поскольку AppComat V21 был выпущен. Если нет, существуют ли какие-либо жизнеспособные обходные пути, которые можно использовать для применения пользовательских тем к индивидуальным предпочтениям?

4b9b3361

Ответ 1

ОБНОВЛЕНИЕ: Я создал библиотеку для борьбы с проблемой (использует AppCompat r22): https://github.com/consp1racy/android-support-preference


ListPreference extends DialogPreference, который использует этот фрагмент кода для создания диалога:

mBuilder = new AlertDialog.Builder(context)
    .setTitle(mDialogTitle)
    .setIcon(mDialogIcon)
    .setPositiveButton(mPositiveButtonText, this)
    .setNegativeButton(mNegativeButtonText, this);

Как вы видите, конструктор AlertDialog.Builder не поставляется со вторым необязательным параметром int theme. Это означает, что диалоговое окно будет отображаться независимо от вашей темы активности в своем атрибуте android:alertDialogTheme.

Теперь вам нужно создать настраиваемую тему для своего диалога, которая получается из Theme.AppCompat.Dialog следующим образом:

<style name="Theme.YourApp.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog">
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowContentOverlay">@null</item>
  <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
  <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
  <item name="colorAccent">@color/accent_yourapp</item>
  <item name="colorPrimary">@color/primary_yourapp</item>
  <item name="colorPrimaryDark">@color/primary_dark_yourapp</item>
</style>

Проблема 1: Вышеупомянутое решение не будет работать для RingtonePreference, потому что оно не распространяется на ListPreference, но вызывает намерение выбора рингтонов, поэтому оно всегда соответствует тематике системы. Проверьте этот ответ: Тема RingtonePreference Итак, мы можем отметить это как решение.

Проблема 2: В диалоговом окне AppCompat отсутствует название. И пока я не нашел способа исправить это. Верно, что я не выгляжу достаточно тяжело. Пусть игнорируется отсутствие заголовка в качестве незначительной проблемы.

Проблема 3: Выводные кнопки переключателя не изменяются, поэтому графики несовместимы между пассивным и активным (цветным) состоянием - все они окрашены (не только одно нажатие), либо все серый. Теперь это действительно раздражает

Проблемы 2 и 3 заставили меня пойти другим путем - моя тема диалога выглядит так на API 14 +

<style name="Theme.MyApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowContentOverlay">@null</item>
</style>

и так далее на API 21 +

<style name="Theme.YourApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
  <item name="android:colorPrimary">@color/primary_yourapp</item>
  <item name="android:colorPrimaryDark">@color/primary_dark_yourapp</item>
  <item name="android:colorAccent">@color/accent_yourapp</item>
</style>

Эти значения были экспериментально получены путем сканирования через исходные файлы платформы и тестирования.

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

EDIT:. Поскольку appcompat-v7-r21.1.0 вы можете использовать AppCompatDialog, который является тематическим вариант нативного AlertDialog.

Вы можете использовать предоставленный AlertDialog.Builder (не путать с его родным экземпляром) для создания своих экземпляров.

Ответ 2

DialogPreference использует android.app.AlertDialog.Builder напрямую, поэтому было бы невозможно переключить отображаемое диалоговое окно на конструкцию материала для всех уровней API (что требует использования android.support.v7.widget.AlertDialog.Builder).

Я считаю, что единственным вариантом на данный момент является расширение и переопределение логики создания диалога в DialogPreference или его подклассах (и использование рефлексии для работы с частными помощниками, если это необходимо). Посмотрите reddit thread и пример AppCompatListPreference сделал автор этой темы.

Ответ 3

Взяв подсказки из уже полученных ответов, я установил в манифесте настраиваемую тему для активности "Настройки", потому что необходимо удалить фон границы вокруг Диалога, который появляется, используя прозрачный фон, это только для API < 21:

AndroidManifest.xml

<application
    android:name=".MyApplication"
    ...
    android:theme="@style/AppTheme">
    ...
    <activity
        android:name=".ui.SettingsActivity"
        ...
        android:theme="@style/AppTheme.Settings">
    </activity>
</application>

значения/style.xml

<!-- Application theme -->
<style name="AppTheme" parent="AppTheme.Base">
    <!-- All customizations that are NOT specific to a particular API-level can go here. -->

</style>

<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="android:alertDialogTheme">@style/AppTheme.AlertDialog</item>
</style>

<!-- Must be different because of transparent background -->
<style name="AppTheme.Settings" parent="AppTheme.Base">
    <item name="android:alertDialogTheme">@style/AppTheme.SettingsAlertDialog</item>
</style>

<style name="AppTheme.AlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
    <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
    <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="colorAccent">@color/accent_light</item>
    <item name="colorControlActivated">@color/accent_light</item>
    <item name="colorControlHighlight">@color/primary_highlight</item>
</style>

<style name="AppTheme.SettingsAlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
    <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
    <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="colorAccent">@color/accent_light</item>
    <item name="colorControlActivated">@color/accent_light</item>
    <item name="colorControlHighlight">@color/primary_highlight</item>
</style>

Наконец, для значений v21/style.xml нормальный диалог, если вы не поместите это в диалоги v21, будет прозрачным.

<style name="AppTheme.Settings" parent="AppTheme.Base">
    <item name="android:alertDialogTheme">@style/AppTheme.AlertDialog</item>
</style>

На самом деле у меня проблема с настройкой цветов непосредственно из атрибутов Theme:? android: colorAccent вместо моего фиксированного цвета @color/accent_light.

Ответ 4

Вы должны проверить этот Gist о том, как разные компоненты используют атрибуты цвета темы. Возможно, вам потребуется добавить следующие атрибуты в вашу тему, чтобы получить необходимый эффект:

<item name="colorControlNormal">@color/x_color</item>
<item name="colorControlActivated">@color/y_color</item>
<item name="colorControlHighlight">@color/z_color</item>