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

153

Je cherche à écrire des préférences qui peuvent être appliquées aux appareils 3.0 et pré-3.0. En découvrant que PreferenceActivitycontient des méthodes obsolètes (bien que celles-ci soient utilisées dans l'exemple de code qui l'accompagne), j'ai regardé PreferenceFragementet le package de compatibilité pour résoudre mes problèmes.

Il semble cependant que ce PreferenceFragmentne soit pas dans le package de compatibilité. Quelqu'un peut-il me dire si c'était intentionnel? Si tel est le cas, puis-je cibler facilement une gamme d'appareils (c'est-à-dire <3.0 et> = 3.0) ou dois-je sauter à travers des cerceaux? Si ce n'était pas intentionnellement exclu, pouvons-nous nous attendre à une nouvelle version du package de compatibilité? Ou existe-t-il une autre solution de contournement qui est sûre à utiliser?

À votre santé

James

James
la source
1
Voici mon approche pour résoudre le problème: stackoverflow.com/questions/14076073/…
ecv
Quelqu'un a créé un tiers PreferenceFragmentque vous oublierez même là. Voyez ma réponse .
theblang
Chris Banes aborde cela dans un commentaire sur son blog . Il a dit que la raison est,"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang
Voir ma réponse mise à jour . PreferenceFragmentCompata été récemment ajouté à la bibliothèque de support.
theblang

Réponses:

90

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

Les méthodes obsolètes sont obsolètes à partir d'Android 3.0. Ils conviennent parfaitement à toutes les versions d'Android, mais la direction est de les utiliser PreferenceFragmentsur Android 3.0 et supérieur.

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 d'appareils (c'est-à-dire <3.0 et> = 3.0) ou dois-je sauter à travers des cerceaux?

Je considère que cela se fait "facilement". Avoir deux PreferenceActivityimplémentations distinctes , l'une utilisant des en-têtes de préférence et PreferenceFragmentsl'autre utilisant l'approche d'origine. Choisissez le bon au moment où vous en avez besoin (par exemple, lorsque l'utilisateur clique sur l'élément du menu d'options). Voici un exemple de projet démontrant cela. Ou, ayez un seul PreferenceActivityqui gère les deux cas, comme dans cet exemple de projet .

Si ce n'était pas intentionnellement exclu, pouvons-nous nous attendre à une nouvelle version du package de compatibilité?

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

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

Voir au dessus.

CommonsWare
la source
Bravo Mark. J'avais vu que vous aviez fait des commentaires à ce sujet à quelques endroits (groupe google android et votre blog) mais que vous vouliez une réponse définitive (dans la mesure où l'on peut obtenir les circonstances).
James
@James: Oui, le hic sera dans la définition XML de préférence, obtenant quelque chose qui fonctionnera bien en tant que fragments et également concaténé ensemble, car je ne suis pas sûr qu'il <include>fonctionne avec la préférence XML. BTW, si vous êtes abonné, la mise à jour du livre faisant référence à ce projet a été annoncée il y a quelques minutes.
CommonsWare
7
Je suis désolé mais je ne suis pas vraiment sûr du point que vous essayez de faire valoir ici. Vous ne répondez à rien du tout, il suffit de commenter / deviner / faire référence à des liens externes non pertinents qui n'ont rien à voir avec le problème. La question est de savoir si l'omission était intentionnelle ou non, sans la version de compatibilité de PreferenceFragment, il n'y a aucun moyen d'étendre PreferenceActivity de la manière que vous avez décrite, car si PreferenceFragment n'existe pas, getSupportFragmentManager () ou l'une des autres méthodes nécessaire d'utiliser des fragments en premier lieu.
Justin Buser
8
@JustinBuser: "La question est de savoir si l'omission était intentionnelle ou non" - les seules personnes qui peuvent y répondre travaillent pour Google. Vous êtes invités à trouver un emploi chez Google pour essayer de le découvrir. "il n'y a aucun moyen d'étendre PreferenceActivity comme vous l'avez décrit" - vous êtes invités à télécharger le code auquel j'ai lié.
CommonsWare
9
@JustinBuser Pour mémoire, Mark a répondu à ma question. C'est évident de ma part d'accepter sa réponse.
James du
21

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 (depuis le SDK 11 ou plus). En fait, c'est ce que la recommandation «facile» 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. Inversement, s'il est important que votre application utilise le fichier compat. API, vous serez confronté à ne pas avoir de classe PreferenceFragment du tout. Ainsi, le ciblage des appareils n'est pas un problème, mais le saut d'obstacles se produit lorsque vous devez choisir l'une ou l'autre API et ainsi soumettre votre conception à des solutions de contournement imprévues. J'ai besoin du compat. API donc je vais créer ma propre classe PreferenceFragment et voir comment cela fonctionne. Dans le pire des cas, je '

EDIT: Après avoir essayé et examiné le code à l' adresse http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java? av = h - créer 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. Il ne semble vraiment pas y avoir de sécurité ou de très bonne motivation pour avoir fait cela et ce n'est pas génial pour les tests unitaires, mais bon ... moins de frappe je suppose ...

EDIT v2: En fait, cela s'est produit et cela a fonctionné. C'était définitivement un casse-tête de faire fonctionner le code avec le JAR de l'API de compatibilité. J'ai dû copier environ 70% du package com.android.preference du SDK vers mon application, puis lutter avec un code Java de qualité médiocre sous Android. J'ai utilisé la v14 du SDK. Il aurait été beaucoup plus facile pour un ingénieur Google de faire ce que j'ai fait, contrairement à ce que j'ai entendu certains ingénieurs Android principaux dire à 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 refactorisation majeure. Journal amusant!

Tenace
la source
Laissez-moi être plus direct. Si tout ce qui vous intéresse est de cibler Honeycomb et plus (quelle part de marché?), Votez pour la réponse de @Commonsware! Si vous vous souciez de la majorité des appareils Android sur le marché aujourd'hui, vous devriez lire ma réponse.
Tenacious
4
Seriez-vous prêt à partager comment vous avez fait cela? Je rencontre exactement le même problème, seul mon PreferenceActivity doit utiliser des chargeurs et je dois donc utiliser la bibliothèque de compatibilité.
Karakuri
3
@Tenacious J'aime votre enquête - bravo. Cependant, je pense que quelqu'un devrait mettre les choses au clair sur votre premier commentaire là-bas - le code de Commonsware fonctionnera sur les appareils pré et post HC - essayez-le d'abord avant de faire des commentaires comme ça. La chose que vous devez réaliser est la liaison tardive utilisée au moment de l'exécution pour prendre en charge les appareils précédents. La vérification de version au moment de l'exécution prend en charge les deux familles d'OS - il s'agit d'un modèle Android courant (pas celui que j'aime - mais un modèle qui est important pour les développeurs Android à apprendre et à se familiariser avec) ... Donc, pour les futurs lecteurs - don ne rejetez aucune des deux approches.
Richard Le Mesurier
@RichardLeMesurier mais la méthode de Commonsware n'est pas appropriée si vous avez besoin de préférences dans DrawerLayout
neworld
16

En me basant 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 d'API Android actuelles avec un minimum de tracas et sans duplication de code ou de ressource. Veuillez 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 sous 4.0.3 et 4.0.4 ainsi qu'un téléphone sous 4.0.4 et 2.3.3 et également un émulateur sous 1.6.

Singe de code oncle
la source
10

En août 2015, Google a publié la nouvelle bibliothèque de support des préférences v7 .

Vous pouvez maintenant utiliser PreferenceFragmentCompat avec tout ActivityouAppCompatActivity

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 preferenceThemedans votre thème:

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

De cette manière, vous pouvez personnaliser le preferenceThemestyle des dispositions utilisées pour chaque type de préférence sans affecter les autres parties de votre activité.

Gabriele Mariotti
la source
1
Je pense que vous manquez une fonction: onCreatePreferences
développeur Android
Cela m'a sauvé la journée! Je me demande pourquoi cela n'est pas visible dans la structure de projet d'Android Studio ... ?? BTW vous avez une faute de frappe dans votre code. Devrait être "étend PreferenceFragmentCompat"
Grzegorz D.
7

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 mise en page normale et lier manuellement les composants de vue aux sharedprefs" est qu'il y a des omissions surprenantes dans l'API android.preferences. PreferenceActivity et PreferenceFragment ont tous deux accès aux méthodes PreferenceManager non publiques critiques, sans lesquelles vous ne pouvez pas implémenter votre propre interface utilisateur de préférence.

En particulier, pour construire une hiérarchie de préférences à partir d'un fichier XML, vous devez utiliser un PreferenceManager, mais tous les constructeurs de PreferenceManager sont soit privés, soit masqués. La méthode pour attacher les écouteurs Preference onClick à votre activité est également privée de package.

Et vous ne pouvez pas contourner ce problème en plaçant sournoisement votre implémentation dans le package android.preferences, car les méthodes non publiques dans les API Android sont en fait omises du SDK. Avec un peu de créativité impliquant la réflexion et les procurations dynamiques, vous pouvez toujours les atteindre. La seule alternative, comme le dit Tenacious, est de bifurquer l'ensemble du package android.preference, y compris au moins 15 classes, 5 mises en page et un nombre similaire d'éléments style.xml et attrs.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 la même difficulté 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 (même si j'espère qu'ils le feront dans les versions futures).

mhsmith
la source
2

Mon application cible est l'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.Fragmentet devais l'utiliser android.support.v4.app.Fragment, mais j'avais également besoin de le mettre PreferenceFragmenten place sans modifications importantes 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();
    }
}
Mohsen Afshin
la source
2

J'avais besoin d'intégrer les préférences dans la conception de l'application et de conserver la prise en charge d'Android 2.3. J'avais donc encore besoin de PreferencesFragment.

Après quelques recherches, j'ai trouvé android-support-v4-preferencesfragment lib. Cette librairie vous fait gagner beaucoup de temps pour copier et refactoriser l'original PreferencesFragment comme l'a dit Tenacious. Fonctionne bien et les utilisateurs apprécient les préférences.

nouveau monde
la source