web-dev-qa-db-fra.com

PreferenceFragment a-t-il été intentionnellement exclu du package de compatibilité?

Je cherche à écrire des préférences qui peuvent être appliquées aux appareils 3.0 et antérieurs à 3.0. Découvrant que PreferenceActivity contient des méthodes obsolètes (bien que celles-ci soient utilisées dans l'exemple de code fourni), j'ai consulté PreferenceFragement et le package de compatibilité pour résoudre mes problèmes.

Il semble cependant que PreferenceFragment ne figure pas dans le package de compatibilité. Quelqu'un peut-il me dire si c'était intentionnel? Si oui, puis-je facilement cibler une gamme de périphériques (c'est-à-dire <3.0 et> = 3.0) ou devrai-je sauter à travers des cerceaux? Si cela n’était pas intentionnellement exclu, pouvons-nous nous attendre à une nouvelle version du paquet de compatibilité? Ou existe-t-il une autre solution de contournement sûre à utiliser?

À votre santé

James

153
James

Découvrir que PreferenceActivity contient des méthodes obsolètes (bien que celles-ci soient utilisées dans l'exemple de code fourni)

Les méthodes obsolètes sont déconseillées à partir de Android 3.0. Ils fonctionnent parfaitement sur toutes les versions d'Android, mais la direction est d'utiliser PreferenceFragment sur Android 3.0 et versions ultérieures.

Quelqu'un peut-il me dire si c'était intentionnel?

Je suppose que c'est une question de temps d'ingénierie, mais ce n'est qu'une supposition.

Si tel est le cas, puis-je cibler facilement une gamme de périphériques (c'est-à-dire <3.0 et> = 3.0) ou devrai-je franchir des étapes?

Je considère que cela se fait "facilement". Avoir deux implémentations PreferenceActivity distinctes, l’une utilisant les en-têtes de préférences et PreferenceFragments, l’autre utilisant l’approche originale. Choisissez celui qui convient le mieux à l'endroit souhaité (par exemple, lorsque l'utilisateur clique sur l'élément du menu Options). Voici un exemple de projet en faire la démonstration. Ou alors, utilisez un seul PreferenceActivity qui gère les deux cas, comme dans ce projet exemple .

Si cela n’était pas intentionnellement exclu, pouvons-nous nous attendre à une nouvelle version du paquet de compatibilité?

Vous saurez quand le reste d'entre nous saura, c'est-à-dire si et quand il sera expédié.

Ou existe-t-il une autre solution de contournement sûre à utiliser?

Voir au dessus.

90
CommonsWare

L'implication subtile de la réponse de @CommonsWare est que - votre application doit choisir entre l'API de compatibilité ou l'API de fragment intégrée (à partir du SDK 11 ou plus). En fait, c'est ce que la recommandation "facilement" a fait. En d'autres termes, si vous souhaitez utiliser PreferenceFragment, votre application doit utiliser l'API de fragment intégrée et gérer les méthodes obsolètes sur PreferenceActivity. À l'inverse, s'il est important que votre application utilise le compat. API, vous serez confronté à ne pas avoir de classe PreferenceFragment. Par conséquent, le ciblage des périphériques n'est pas un problème, mais le saut de cercle survient lorsque vous devez choisir l'une ou l'autre API et soumettre ainsi votre conception à des solutions de contournement imprévues. J'ai besoin de compat API, je vais donc créer ma propre classe PreferenceFragment et voir comment cela fonctionne. Dans le pire des cas, je créerai simplement une mise en page normale (fragment) et lierai les composants de la vue aux fichiers partagés manuellement ... euh.

EDIT: Après avoir essayé et consulté le code sur la page http://grepcode.com/file/repository.grepcode.com/Java/ext/com.google.Android/android/4.0.1_r1/Android/preference/ PreferenceFragment.java?av=h - la création de mon propre PreferenceFragment ne se produira pas. Il semble que l'utilisation libérale de package-private dans PreferenceManager au lieu de "protected" soit le principal bloqueur. Cela ne ressemble vraiment pas à une sécurité ou à une très bonne motivation. Ce n'est pas bon pour les tests unitaires, mais bon ... moins de dactylographie, je suppose ...

EDIT v2: En fait, cela est arrivé et cela a fonctionné. Faire fonctionner le code avec le fichier JAR de l'API de compatibilité était assurément un casse-tête. Je devais copier environ 70% du package com.Android.preference du SDK vers mon application, puis lutter avec un code de qualité médiocre Java) dans Android. J'ai utilisé la version v14 du SDK. Il aurait été beaucoup plus facile pour un ingénieur de Goog de faire ce que j’ai fait, contrairement à ce que j’ai entendu dire: Android ingénieurs disent de ce sujet.

BTW - ai-je dit "le ciblage des appareils n'est pas un problème"? C'est totalement ... si vous utilisez com.Android.preference, vous ne pourrez pas échanger avec l'API de compatibilité sans refactoring majeur. Journal amusant!

21
Tenacious

S'appuyant sur la réponse de CommonsWare ainsi que sur les observations de Tenacious, j'ai mis au point une solution de classe descendante unique capable de cibler toutes les versions Android actuelles d'API) avec un minimum d'agitation, sans duplication de code ou de ressource. Voir ma réponse à la question connexe ici: PreferenceActivity Android 4.0 et versions antérieures

ou sur mon blog: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Testé sur deux tablettes fonctionnant sous 4.0.3 et 4.0.4, ainsi que sur un téléphone fonctionnant sous 4.0.4 et 2.3.3 et sur un émulateur exécutant la version 1.6.

16
Uncle Code Monkey

Voir PreferenceFragment-Compat de Machinarius. C'était facile d'arriver avec Gradle et j'oublie que c'est même là.

compile 'com.github.machinarius:preferencefragment:0.1.1'

Mise à jour importante: Le dernière révision du v7 support library a maintenant un natif PreferenceFragmentCompat .

10
theblang

En août 2015, Google a publié le nouveau Preference Support Library v7 .

Maintenant, vous pouvez utiliser le PreferenceFragmentCompat avec n’importe quel Activity ou AppCompatActivity

public static class PrefsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

Vous devez définir preferenceTheme dans votre thème:

<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
  ...
  <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

De cette façon, vous pouvez personnaliser le preferenceTheme pour styliser les présentations utilisées pour chaque type de préférence sans affecter les autres parties de votre activité.

10
Gabriele Mariotti

La réponse de Tenacious est correcte, mais voici quelques détails supplémentaires.

La raison pour laquelle vous ne pouvez pas "créer une présentation normale et lier manuellement les composants de la vue aux fichiers partagés" est due à certaines omissions surprenantes dans l'API Android.preferences. PreferenceActivity et PreferenceFragment ont tous deux accès aux méthodes critiques PreferenceManager non publiques, sans lesquelles vous ne pouvez pas implémenter votre propre interface utilisateur de préférences.

En particulier, pour construire une hiérarchie de préférences à partir d'un fichier XML, vous devez utiliser un gestionnaire PreferenceManager, mais tous les constructeurs de PreferenceManager sont soit privés du paquet, soit cachés. La méthode permettant d'associer les écouteurs de préférence onClick à votre activité est également privée à l'emballage.

Et vous ne pouvez pas contourner ce problème en plaçant votre implémentation dans le package Android.preferences de manière sournoise, car les méthodes non publiques dans Android sont en fait omises du SDK. Avec un peu de créativité impliquant La réflexion et les procurations dynamiques, vous pouvez toujours y accéder.La seule alternative, comme le dit Tenacious, consiste à bifurquer l’ensemble du package Android.preference, comprenant au moins 15 classes, 5 mises en page et un nombre similaire de style.xml et attrs. éléments xml.

Donc, pour répondre à la question initiale, la raison pour laquelle Google n'a pas inclus PreferenceFragment dans le package de compatibilité est qu'ils auraient eu exactement les mêmes difficultés que Tenacious et moi-même. Même Google ne peut pas remonter le temps et rendre ces méthodes publiques sur les anciennes plates-formes (bien que j'espère qu'ils le feront dans les prochaines versions).

7
mhsmith

J'avais besoin d'intégrer les préférences dans la conception de l'application et de conserver le support pour Android 2.3. Il me fallait donc encore PreferencesFragment.

Après quelques recherches, j'ai trouvé Android-support-v4-preferencefragment lib. Cette bibliothèque permet de gagner beaucoup de temps pour la copie et la refactorisation de PreferencesFragment comme dit Tenacious. Fonctionne bien et les utilisateurs apprécient les préférences.

2
neworld

Ma cible d'application est API +14 mais, en raison de l'utilisation de la bibliothèque de support pour une navigation sophistiquée, je ne pouvais pas utiliser le Android.app.Fragment et a dû utiliser Android.support.v4.app.Fragment, mais j’avais également besoin d’avoir PreferenceFragment en place sans modifications majeures du code.

Donc, ma solution facile pour avoir les deux mondes de bibliothèque de support et PreferenceFragment:

private Android.support.v4.app.Fragment fragment;
private Android.app.Fragment nativeFragment = null;

private void selectItem(int position) {
    fragment = null;
    boolean useNativeFragment = false;
    switch (position) {
    case 0:
        fragment = new SampleSupprtFragment1();
        break;
    case 1:
        fragment = new SampleSupprtFragment2();
        break;
    case 2:
        nativeFragment = new SettingsFragment();
        useNativeFragment = true;
        break;
    }
    if (useNativeFragment) {
        Android.app.FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, nativeFragment).commit();
    } else {
        if (nativeFragment != null) {
            getFragmentManager().beginTransaction().remove(nativeFragment)
                .commit();
            nativeFragment = null;
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment).commit();
    }
}
2
Mohsen Afshin