Que faut-il utiliser au lieu de «addPreferencesFromResource» dans une PreferenceActivity?

360

Je viens de remarquer le fait que la méthode addPreferencesFromResource(int preferencesResId)est marquée comme obsolète dans la documentation d'Android ( Reference Entry ).

Malheureusement, aucune méthode alternative n'est fournie dans la description de la méthode.

Quelle méthode doit être utilisée à la place afin de connecter un preferenceScreen.xml à la PreferenceActivity correspondante?

mweisz
la source
2
Une approche très simple est fournie par WannaGetHigh à stackoverflow.com/questions/23523806/…
Sabin
La solution y est toujours utilisée addPreferencesFromResource(int preferencesResId). Suis-je en train de manquer quelque chose?
Jammo
@Jammo Oui, mais il a été déplacé de l'activité vers le fragment pour refléter la nouvelle façon de le faire -> fragment.
WannaGetHigh

Réponses:

332

Aucune méthode alternative n'est fournie dans la description de la méthode car l'approche préférée (à partir de l'API niveau 11) consiste à instancier des objets PreferenceFragment pour charger vos préférences à partir d'un fichier de ressources. Voir l'exemple de code ici: PreferenceActivity

glorifiedHacker
la source
Merci beaucoup pour votre réponse. Je suivais juste les instructions obsolètes de "Video2Brain-Android Development" qui m'ont amené à utiliser la version PreferenceActivity de la méthode. Btw: Je serais ravi de juger votre réponse utile si je le pouvais.
mweisz
33
Maintenant, seulement si le PreferenceFragment était inclus dans le package de compatibilité, il serait logique de l'utiliser stackoverflow.com/questions/5501431/…
christoff
1
Depuis que j'utilise Action Bar Sherlock, j'ai suivi le blog suivant pour résoudre ce problème, voir ... commonsware.com/blog/2012/10/16/…
Someone Somewhere
2
Vous devez toujours appeler addPreferencesFromResource (int PreferencesID) si vous souhaitez que l'application soit rétrocompatible avec un niveau d'api antérieur à 11 (Android 3.0). Mais je suppose que vous pouvez également considérer ces anciens appareils comme obsolètes.
Einar Sundgren
5
@EinarSundgren Je suis le propriétaire de Nexus One et ma version maximale disponible est de 2,2: vous ne m'arrêterez pas! Je ne serai JAMAIS déprécié! Par le pouvoir de Grayskull ... j'ai le pouvoir!
TechNyquist
186

Pour ajouter plus d'informations à la bonne réponse ci-dessus, après avoir lu un exemple d'Android-er, j'ai trouvé que vous pouvez facilement convertir votre activité de préférence en un fragment de préférence. Si vous avez l'activité suivante:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.my_preference_screen);
    }
}

Les seules modifications que vous devez apporter sont de créer une classe de fragment interne, de déplacer le addPreferencesFromResources()dans le fragment et d'appeler le fragment de l'activité, comme ceci:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    }

    public static class MyPreferenceFragment extends PreferenceFragment
    {
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.my_preference_screen);
        }
    }
}

Il peut y avoir d'autres subtilités à faire des préférences plus complexes à partir de fragments; si c'est le cas, j'espère que quelqu'un les note ici.

Garret Wilson
la source
6
Bien, merci pour ça. Java et ce type de pensée sont si éloignés de mon joli petit monde .net :)
Tom
44
Pourquoi se déprécieraient-ils addPreferencesFromResources()dans le commerce de PreferenceFragment? Cela semble inutile d'un point de vue débutant.
Howdy_McGee
4
L'appel requiert l'API niveau 11
mehmet
1
alors qu'en est-il si l'API est au niveau 9? @mehmet
gumuruh
2
Réponse géniale! existe-t-il un moyen d'y parvenir sans changer android.R.id.content? me semble inélégant pour une raison quelconque ... (corrigez-moi si je me trompe)
tomer.z
37

@Garret Wilson Merci beaucoup! En tant que noob au codage Android, je suis coincé avec le problème d'incompatibilité des préférences depuis tant d'heures, et je trouve tellement décevant qu'ils aient déconseillé l'utilisation de certaines méthodes / approches pour les nouvelles qui ne sont pas prises en charge par les anciennes API. avoir à recourir à toutes sortes de solutions de contournement pour faire fonctionner votre application dans une large gamme d'appareils. C'est vraiment frustrant!

Votre classe est géniale, car elle vous permet de continuer à travailler dans de nouvelles API avec des préférences comme c'était le cas auparavant, mais elle n'est pas rétrocompatible. Étant donné que j'essaie d'atteindre un large éventail d'appareils, je l'ai légèrement modifié pour le faire fonctionner dans les appareils pré-API 11 ainsi que dans les API plus récentes:

import android.annotation.TargetApi;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;

public class MyPrefsActivity extends PreferenceActivity
{
    private static int prefs=R.xml.myprefs;

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        try {
            getClass().getMethod("getFragmentManager");
            AddResourceApi11AndGreater();
        } catch (NoSuchMethodException e) { //Api < 11
            AddResourceApiLessThan11();
        }
    }

    @SuppressWarnings("deprecation")
    protected void AddResourceApiLessThan11()
    {
        addPreferencesFromResource(prefs);
    }

    @TargetApi(11)
    protected void AddResourceApi11AndGreater()
    {
        getFragmentManager().beginTransaction().replace(android.R.id.content,
                new PF()).commit();
    }

    @TargetApi(11)
    public static class PF extends PreferenceFragment
    {       
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(MyPrefsActivity.prefs); //outer class
            // private members seem to be visible for inner class, and
            // making it static made things so much easier
        }
    }
}

Testé avec succès dans deux émulateurs (2.2 et 4.2).

Pourquoi mon code a l'air si merdique:

Je suis un fan du codage Android et je ne suis pas le plus grand fan de Java.

Afin d'éviter l'avertissement obsolète et de forcer Eclipse à me permettre de compiler, j'ai dû recourir à des annotations, mais celles-ci semblent n'affecter que les classes ou les méthodes, j'ai donc dû déplacer le code sur deux nouvelles méthodes pour en tirer parti.

Je ne voudrais pas avoir à écrire mon identifiant de ressource xml deux fois à chaque fois que je copie et colle la classe pour une nouvelle PreferenceActivity, j'ai donc créé une nouvelle variable pour stocker cette valeur.

J'espère que cela sera utile à quelqu'un d'autre.

PS: Désolé pour mes opinions, mais quand vous venez de découvrir de nouveaux handicaps, vous ne pouvez pas vous empêcher de vous frustrer!

ecv
la source
Eh bien ... je viens de remarquer que je devrais changer le nom de la classe externe deux fois chaque fois que je le copie et le colle. Il existe en fait un moyen d'éviter cela, en passant des préférences à la classe interne. Vous ne pouvez pas créer un constructeur de classe interne acceptant prefs comme paramètre car il n'est apparemment pas recommandé pour les classes dérivées de PreferenceFragment. En outre, vous ne pouvez pas créer une méthode dans la classe interne pour obtenir des préférences et appeler immédiatement addPreferencesFromResource, car addPreferencesFromResource doit être appelé après l'appel de super.onCreate et onCreate n'est pas appelé juste après que la classe dérivée PreferenceFragment a ...
ecv
... été instancié. Vous devez donc créer une nouvelle variable dans la classe interne, créer une méthode d'ensemble publique pour elle dans la classe interne, laisser le addPreferencesFromResource où il se trouve après l'appel à super.onCreate, à l'intérieur de onCreate, instancier la classe interne, définir les préférences , et utilisez-le dans l'appel à getFragmentManager () ... comme précédemment.
ecv
2
Votre code vous a sauvé la vie !! J'essayais de cibler à la fois l'ancien et le nouveau téléphone, et j'ai perdu 3 jours ... Et la réponse est si simple. Merci
Devdatta Tengshe
22

Mon approche est très proche de celle de Garret Wilson (merci, je vous ai voté;)

De plus, il offre une compatibilité descendante avec Android <3.

Je viens de reconnaître que ma solution est encore plus proche de celle de Kevin Remo . C'est juste un tout petit peu plus propre (car il ne repose pas sur l' anti-motif "expection" ).

public class MyPreferenceActivity extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            onCreatePreferenceActivity();
        } else {
            onCreatePreferenceFragment();
        }
    }

    /**
     * Wraps legacy {@link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl
     * < 11).
     */
    @SuppressWarnings("deprecation")
    private void onCreatePreferenceActivity() {
        addPreferencesFromResource(R.xml.preferences);
    }

    /**
     * Wraps {@link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >=
     * 11).
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void onCreatePreferenceFragment() {
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new MyPreferenceFragment ())
                .commit();
    }
}

Pour un exemple "réel" (mais plus complexe), voir NusicPreferencesActivity et NusicPreferencesFragment .

schnatterer
la source
@SuppressLint("NewApi")- éviter - être plus précis. L'avez-vous exécuté pour des API bas? Il jetteraVerifyError
Mr_and_Mrs_D
J'ai testé ce qui précède sur l'émulateur exécutant l'API niveau 10, l'APK a été créé à l'aide de la version 19 du SDK. Quelle version du SDK avez-vous utilisée pour la construction? Sur quel niveau d'API d'appareil l'avez-vous exécuté? L'avez-vous exécuté sur l'émulateur ou sur un périphérique physique? Quand exactement l'erreur s'est-elle produite? Si vous créez avec SDK <= 16, consultez cette réponse .
schnatterer
Vous manquez le point - et ajoutez @Mr_and_Mrs_D si vous voulez que je sois informé. Pour le VE, lisez ici: stackoverflow.com/questions/20271593/… . Le @SuppressLint("NewApi")style est tout simplement mauvais
Mr_and_Mrs_D
@M Eh bien, que diriez-vous d'une proposition sur la façon d'éviter @SuppressLint("NewApi")dans cette situation particulière?
schnatterer
@TargetApi(Build.VERSION_CODES.HONEYCOMB)- pas tous les avertissements pour toute API :)
Mr_and_Mrs_D
6

Au lieu d'exceptions, utilisez simplement:

if (Build.VERSION.SDK_INT >= 11)

et utilise

@SuppressLint("NewApi")

pour supprimer les avertissements.

Peter
la source
1
car il ne fait rien d'autre que planter: D
Ishtiaq
1
si c'est crash ... alors essayez de l'attraper: D
gumuruh
0

Au lieu d'utiliser un PreferenceActivitypour charger directement les préférences, utilisez un AppCompatActivityou équivalent qui charge unPreferenceFragmentCompat qui charge vos préférences. Il fait partie de la bibliothèque de support (maintenant Android Jetpack) et offre une compatibilité avec API 14.

Dans votre build.gradle, ajoutez une dépendance pour la bibliothèque de prise en charge des préférences:

dependencies {
    // ...
    implementation "androidx.preference:preference:1.0.0-alpha1"
}

Remarque: nous allons supposer que vos préférences XML sont déjà créées.

Pour votre activité, créez une nouvelle classe d'activités. Si vous utilisez des thèmes matériels, vous devez étendre un AppCompatActivity, mais vous pouvez être flexible avec ceci:

public class MyPreferencesActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_preferences_activity)
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, MyPreferencesFragment())
                    .commitNow()
        }
    }
}

Maintenant, pour la partie importante: créez un fragment qui charge vos préférences à partir de XML:

public class MyPreferencesFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.my_preferences_fragment); // Your preferences fragment
    }
}

Pour plus d'informations, lisez les documents des développeurs Android pour PreferenceFragmentCompat.

Willie Chalmers III
la source