web-dev-qa-db-fra.com

Application du thème AppCompat à des préférences individuelles dans un PreferenceFragment

Je me suis efforcé d’essayer d’obtenir ma PreferenceFragment afin d’avoir le même thème et le même style fondés sur des matériaux (via AppCompat) que le reste de ma candidature. La PreferenceFragment que j'utilise pour gérer tous les paramètres de mon application est indiquée ci-dessous:

enter image description here

Comme vous pouvez le voir sur la capture d'écran ci-dessus, j'ai pu personnaliser PreferenceFragment en utilisant colorAccent, colorPrimary et quelques autres attributs. Mon thème pour la PreferenceFragment est le suivant:

<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>

Cependant, malgré tous mes efforts, je ne parviens pas à appliquer de thèmes aux éléments individuels de ma PreferenceFragment, tels que ListPreference et EditTextPreference; toutes les préférences conservent toujours le thème standard AppCompat:

enter image description here

J'ai trouvé un article de 2 ans traitant de ce problème, mais sans réelle solution: Comment appliquer le thème à des éléments <PreferenceScreen> d'une <PreferenceCategory>

Je me demande si quelqu'un a réussi à appliquer des thèmes aux préférences depuis la sortie d'AppComat V21. Si ce n'est pas le cas, existe-t-il des solutions de contournement viables pouvant être utilisées pour appliquer des thèmes personnalisés à des préférences individuelles?

18
Willis

UPDATE: J'ai créé une bibliothèque pour résoudre le problème (utilise AppCompat r22): https://github.com/consp1racy/Android-support-preference


ListPreference extend DialogPreference qui utilise cette partie de code pour créer la boîte de dialogue:

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

Comme vous pouvez le constater, le constructeur AlertDialog.Builder n’est pas fourni avec le deuxième paramètre optionnel int theme. Cela signifie que le dialogue sera thématisé par tout ce que le thème de votre activité a dans son attribut Android:alertDialogTheme.

Vous devez maintenant créer un thème personnalisé pour votre boîte de dialogue, qui dérive de Theme.AppCompat.Dialog comme suit:

<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>

Problème 1: La solution ci-dessus ne fonctionnera pas pour RingtonePreference car elle n’étend pas ListPreference mais appelle une intention de sélecteur de sonnerie, elle est donc toujours thématisée en fonction du système. Découvrez cette réponse: Thème RingtonePreference Nous pouvons donc marquer ceci comme résolu.

Problème 2: Les boîtes de dialogue AppCompat n'ont pas de titre. Et jusqu'à présent, je n'ai pas trouvé le moyen de résoudre ce problème. C'est vrai que je ne cherche pas assez fort. Ignorons l'absence de titre en tant que problème mineur.

Problème 3: Les boutons de la case d'option radio ne sont pas mutés, ainsi les graphiques ne sont pas cohérents entre l'état passif et actif (en couleur) - tous sont colorés (pas uniquement celui que vous appuyez) ou tous sont en gris. Maintenant c'est vraiment ennuyeux

Les problèmes 2 et 3 m'ont obligé à emprunter un autre chemin: mon thème de dialogue ressemble à ceci dans l'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>

et comme ça sur 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>

Ces valeurs ont été acquises expérimentalement en explorant les fichiers sources de la plate-forme et ont été bien testées.

Le fait est que la seule solution fiable semble utiliser le thème de dialogue par défaut du périphérique. Le seul choix avant Lollipop est une variante claire ou sombre. Sur Lollipop cela fonctionnera comme prévu et demandé.

EDIT: Depuis appcompat-v7-r21.1.0, vous pouvez utiliser AppCompatDialog qui est une variante à thème matériel de la variable native AlertDialog.

Vous pouvez utiliser le paramètre AlertDialog.Builder fourni (à ne pas confondre avec son équivalent natif) pour créer ses instances.

9
Eugen Pechanec

DialogPreference utilise Android.app.AlertDialog.Builder directement, il serait donc impossible de basculer la boîte de dialogue affichée sur celle de conception des matériaux pour tous les niveaux d'API (ce qui nécessite d'utiliser Android.support.v7.widget.AlertDialog.Builder).

Je crois que la seule option pour le moment est d'étendre et de remplacer la logique de création de dialogue dans DialogPreference ou ses sous-classes (et d'utiliser la réflexion pour contourner les accesseurs privés si nécessaire). Découvrez ce fil reddit et un exemple AppCompatListPreference créé par l'auteur de ce fil.

3
hidro

En me basant sur les réponses déjà données, j'ai défini dans le manifeste un thème personnalisé pour l'activité Paramètres, car il est nécessaire de supprimer l'arrière-plan de la bordure entourant la boîte de dialogue qui apparaît, en utilisant un arrière-plan transparent, ceci uniquement pour l'API <21:

AndroidManifest.xml

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

values ​​/ 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>

enfin pour values ​​- v21 / style.xml le dialogue normal, si vous ne le mettez pas dans la v21, les dialogues seront transparents.

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

En fait, j'ai le problème de définir les couleurs directement à partir des attributs de thème:? Android: colorAccent au lieu de ma couleur fixe @ color/accent_light.

2
Davideas

Vous devriez consulter this Gist sur la manière dont différents composants utilisent les attributs de couleur de thème. Vous devrez peut-être ajouter les attributs suivants à votre thème pour obtenir l'effet souhaité:

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