web-dev-qa-db-fra.com

Comment utiliser support.v7.preference avec AppCompat et inconvénients potentiels

J'essayais d'implémenter les préférences pour une application AppCompat en utilisant support.v7.preference. Il m'a fallu quelques jours pour le parcourir, car support.v7.preference présente quelques différences importantes par rapport aux préférences natives ... ce qui n'est pas si mal une fois que vous savez, mais malheureusement, il existe peu de documentation. Je pensais partager mes découvertes pour que les autres ne subissent pas la même douleur.


Alors ... question:

Comment mettre en œuvre au mieux les préférences pour les applications AppCompat (avec PreferenceFragment et AppCompatAcitivity incompatibles)?

Voici quelques questions connexes:

Documents officiels ici:

16
maxdownunder

Solution 1: Native PreferenceFragment avec AppCompatActivity

Dans AndroidStudio, choisissez Fichier> Nouveau projet> ...> SettingsActivity. Ce modèle utilise une solution de contournement qui permet à la PreferenceFragment native de fonctionner avec AppCompatActivity, similaire au support.v4.Fragment ou au support.v7.PreferenceFragmentCompat.

  • Pro: vous pouvez maintenant utiliser la fonctionnalité de préférence native dans une application AppCompat. C'est une approche rapide lorsque vous utilisez le modèle AS, et vous pouvez vous en tenir aux documents et flux de travail de préférence existants.
  • Contre: la mise à niveau n'est pas très intuitive ou propre. De plus, comme il est généralement conseillé d'utiliser des bibliothèques de support là où elles sont disponibles, je ne suis pas sûr de la validité de cette approche pour le futur.

Solution 2: support.v7.preference.PreferenceFragmentCompat avec AppCompatActivity

  • Pro: maximise la compatibilité
  • Con: beaucoup de lacunes à combler. Cela pourrait également ne fonctionner avec aucune des préférences-extensions-libs existantes existantes (par exemple, ColorPicker ou FontPreferences).

Si vous choisissez de ne pas utiliser la solution 1 (je ne sais toujours pas lequel des deux est le plus fiable pour l'avenir), l'utilisation de support.v7.preference présente quelques inconvénients. 

Les inconvénients importants liés à l’utilisation de la solution 2 sont mentionnés ci-dessous.

Dépendances:

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

Thème: Vous devrez définir un preferenceTheme dans votre fichier styles.xml, sinon l'exécution de votre application déclenchera une exception.

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
    <!-- Customize your theme here. -->
    <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

Vous voudrez peut-être diviser cela en différents styles pour 7 +/14 +/21 +. Beaucoup de gens se plaignent d'être buggés au moment d'écrire ces lignes. Une réponse très complète est disponible ici .

Changements de comportement: l'utilisation des préférences natives est extrêmement simple: tout ce que vous avez à faire est de définir/gérer votre preferences.xml et d'utiliser addPreferencesFromResource(R.xml.preferences) dans votre PreferenceFragment. Les préférences personnalisées se font facilement en sous-classant DialogPreference, puis en faisant simplement référence au preferences.xml... bam.

Malheureusement, support.v7.preference a tout ce qui est en rapport avec le traitement de Fragment, ce qui lui fait perdre beaucoup de ses fonctionnalités intégrées. Au lieu de simplement gérer un fichier XML, vous devez maintenant sous-classer et remplacer beaucoup d'éléments, qui sont malheureusement tous non documentés.

PreferenceScreens:PreferenceScreens ne sont plus gérés par le cadre. Définir une PreferenceScreen dans votre preference.xml (comme décrit dans la docs ) affichera l'entrée, mais cliquer dessus ne fera rien. C'est maintenant à vous de gérer l'affichage et la navigation dans les sous-écrans. Ennuyeuse.

Il existe une approche (décrite ici ), en ajoutant un PreferenceFragmentCompat.OnPreferenceStartScreenCallback à votre PreferenceFragmentCompat. Bien que cette approche soit rapidement mise en œuvre, elle échange simplement le contenu du fragment de préférence existant. L'inconvénient, c'est qu'il n'y a pas de navigation arrière, vous êtes toujours au sommet, ce qui n'est pas très intuitif pour l'utilisateur.

Dans une autre approche (décrite ici ), vous devrez également gérer la pile arrière afin de pouvoir effectuer la navigation arrière comme prévu. Ceci utilise preferenceScreen.getKey() comme racine pour chaque fragment nouvellement créé/affiché.

Ce faisant, vous pouvez également trébucher sur le fait que la PreferenceFragments soit transparente par défaut et s’additionne étrangement les unes sur les autres. Les gens ont tendance à ignorer PreferenceFragmentCompat.onViewCreated() pour ajouter quelque chose comme

// 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: Définir vos propres préférences est également passé de trivial à ennuyeux. DialogPreference a maintenant tout ce qui concerne le dialogue, supprimé. Ce bit vit maintenant dans PreferenceDialogFragmentCompat. Vous devrez donc sous-classer les deux, puis créer le dialogue et l'afficher vous-même (expliqué ici ).

En regardant la source de PreferenceFragmentCompat.onDisplayPreferenceDialog(), on voit qu’il sait comment gérer exactement 2 préférences de dialogue (EditTextPreference, ListPreference), tout le reste que vous devrez implémenter vous-même en utilisant OnPreferenceDisplayDialogCallbacks ... on se demande pourquoi il n’existe classe de DialogPreference!


Voici un code qui implémente la plupart de ces solutions de contournement et les encadre dans un module lib:

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

Les principales intentions étaient:

  • Supprimez le besoin d'étendre et de manipuler Activity et PreferenceFragment dans chaque application/projet. preference.xml est à nouveau le seul fichier par projet à modifier/maintenir.
  • Manipulez et affichez PreferenceScreens (sous-écrans) comme prévu.
  • Décompressez DialogPreference pour restaurer le comportement natif.
  • Manipulez et affichez toute sous-classe de DialogPreference.

Ne croyez pas que c'est assez propre pour être utilisé immédiatement, mais cela pourrait vous donner des indices lorsque vous traitez des problèmes similaires. Faites un tour et laissez-moi savoir si vous avez des suggestions.

53
maxdownunder

J'ai une solution alternative à celle-ci, sur laquelle j'aimerais beaucoup les retours.

J'ai fait une mise en page personnalisée pour mon fragment de préférence, avec un bouton "Retour" dans le coin supérieur gauche. 

Premièrement, dans le "onCreatePreference", je stocke la racine PreferenceScreen:

root = this.getPreferenceScreen();

Ensuite, j'ajoute OnPreferenceStartScreenCallback comme décrit ci-dessus et dans d'autres threads pour que le fragment passe au sous-écran, mais dans mon "onPreferenceStartScreen", je règle également le bouton Retour sur visible comme ceci:

    public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
        preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
        backButton.setVisibility(View.VISIBLE);
        return true;
}

Enfin, le gestionnaire de clics backButton:

    setPreferenceScreen(root);
    back.setVisibility(View.GONE);

Cela semble bien fonctionner pour moi. Évidemment, la pile arrière ne fonctionnera pas, mais je peux vivre avec cela car il y a un bouton Précédent.

Pas parfait, mais vu l’API abominable, je pense que je suis heureux.

J'aimerais entendre si quelqu'un pense qu'il y a des problèmes avec cette approche.

0
Mathias