web-dev-qa-db-fra.com

Comment créer des préférences personnalisées à l'aide de la bibliothèque Android.support.v7.preference?

Je veux prendre en charge au moins l'api 10, je veux pouvoir styler mes préférences, je veux pouvoir avoir des en-têtes (ou afficher PreferenceScreens). Il semble que PreferenceActivity, non entièrement pris en charge par la coloration de AppCompat, ne convienne pas. J'essaie donc d'utiliser AppCompatActivity et PreferenceFragmentCompat.

public class Prefs extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null)
            getSupportFragmentManager().beginTransaction()
                    .replace(Android.R.id.content, new PreferencesFragment())
                    .commit();
    }

    public static class PreferencesFragment extends PreferenceFragmentCompat {
        @Override public void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
        }

        @Override
        public void onDisplayPreferenceDialog(Preference preference) {
            // the following call results in a dialogue being shown
            super.onDisplayPreferenceDialog(preference);
        }

        @Override public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
            // I can probably use this to go to to a nested preference screen
            // I'm not sure...
        }
    }
}

Maintenant, je veux créer une préférence personnalisée qui fournira le choix d'une police. Avec PreferenceActivity, je pourrais simplement faire

import Android.preference.DialogPreference;

public class FontPreference extends DialogPreference {

    public FontPreference(Context context, AttributeSet attrs) {super(context, attrs);}

    @Override protected void onPrepareDialogBuilder(Builder builder) {
        super.onPrepareDialogBuilder(builder);
        // do something with builder and make a Nice cute dialogue, for example, like this
        builder.setSingleChoiceItems(new FontAdapter(), 0, null);
    }
}

et utilisez xml comme celui-ci pour l'afficher

<my.app.FontPreference Android:title="Choose font" Android:summary="Unnecessary summary" />

Mais maintenant, il n'y a pas de onPrepareDialogBuilder dans Android.support.v7.preference.DialogPreference. Au lieu de cela, il a été déplacé vers PreferenceDialogFragmentCompat. J'ai trouvé peu d'informations sur la façon d'utiliser cette chose, et je ne sais pas comment passer du xml à l'affichage. Le fragment de préférence v14 a le code suivant:

public void onDisplayPreferenceDialog(Preference preference) {
    ...

    final DialogFragment f;
    if (preference instanceof EditTextPreference)
        f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
    ...
    f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}

J'ai essayé le sous-classement Android.support.v7.preference.DialogPreference et que onDisplayPreferenceDialog utilise un morceau de code similaire pour instancier un mannequin FontPreferenceFragment mais il échoue avec l'exception suivante.

Java.lang.IllegalStateException: Target fragment must implement TargetFragment interface

À ce stade, je suis déjà trop profondément dans le pétrin et je ne veux pas creuser davantage. Google ne sait rien de cette exception. Quoi qu'il en soit, cette méthode semble être trop compliquée. Alors, quelle est la meilleure façon de créer des préférences personnalisées à l'aide de la bibliothèque Android.support.v7.preference?

23
squirrel

Remarque importante: Actuellement (v23.0.1 de la bibliothèque v7) il y a encore beaucoup de problèmes de thème avec le 'PreferenceThemeOverlay' (voir ce problème ). Sur Lollipop par exemple, vous vous retrouvez avec des en-têtes de catégorie de style Holo.

Après quelques heures frustrantes, j'ai finalement réussi à créer une préférence v7 personnalisée. Créer votre propre Preference semble plus difficile que vous ne le pensez. Assurez-vous donc de prendre un peu de temps.

Au début, vous vous demandez peut-être pourquoi vous trouverez à la fois un DialogPreference et un PreferenceDialogFragmentCompat pour chaque type de préférence. Il s'avère que le premier est la préférence réelle, le second est le DialogFragment où la préférence serait affichée. Malheureusement, vous devez sous-classer les deux d'entre eux .

Ne vous inquiétez pas, vous n'aurez pas besoin de changer de code. Il vous suffit de déplacer certaines méthodes:

  • Toutes les méthodes d'édition des préférences (comme setTitle() ou persist*()) se trouvent dans la classe DialogPreference.
  • Toutes les méthodes de dialogue (-édition) (onBindDialogView(View) & onDialogClosed(boolean)) ont été déplacées vers PreferenceDialogFragmentCompat.

Vous voudrez peut-être que votre classe existante prolonge la première, de cette façon, vous n'avez pas besoin de changer trop de choses, je pense. La saisie semi-automatique devrait vous aider à trouver les méthodes manquantes.

Lorsque vous avez terminé les étapes ci-dessus, il est temps de lier ces deux classes ensemble. Dans votre fichier xml, vous vous référerez à la partie préférences. Cependant, Android ne sait pas encore quel Fragment il doit gonfler lorsque votre préférence personnalisée doit être. Par conséquent, vous devez remplacer onDisplayPreferenceDialog(Preference):

@Override
public void onDisplayPreferenceDialog(Preference preference) {
    DialogFragment fragment;
    if (preference instanceof LocationChooserDialog) {
        fragment = LocationChooserFragmentCompat.newInstance(preference);
        fragment.setTargetFragment(this, 0);
        fragment.show(getFragmentManager(),
                "Android.support.v7.preference.PreferenceFragment.DIALOG");
    } else super.onDisplayPreferenceDialog(preference);
}

et aussi votre DialogFragment doit gérer la 'clé':

public static YourPreferenceDialogFragmentCompat newInstance(Preference preference) {
    YourPreferenceDialogFragmentCompat fragment = new YourPreferenceDialogFragmentCompat();
    Bundle bundle = new Bundle(1);
    bundle.putString("key", preference.getKey());
    fragment.setArguments(bundle);
    return fragment;
}

Cela devrait faire l'affaire. Si vous rencontrez des problèmes, essayez de jeter un œil aux sous-classes existantes et voyez comment Android l'a résolu (dans Android Studio: tapez le nom d'une classe et appuyez sur Ctrl + b pour voir la classe décompilée. J'espère que ça aide.

40
Coen B

Il existe un bon didacticiel et un projet Github qui explique en détail comment créer une classe de préférences personnalisée qui étend la bibliothèque de préférences de support:

Les points clés sont:

  • Vous aurez besoin d'un PreferenceDialogFragmentCompat personnalisé, qui contrôle le lancement d'une boîte de dialogue lorsque vous cliquez sur la ligne de préférence. Il configure également la vue de la boîte de dialogue dans onBindDialogView().

  • Dans votre écran de préférences, remplacez onDisplayPreferenceDialog() pour lancer votre PreferenceDialogFragmentCompat personnalisé.

  • Vous aurez besoin d'un DialogPreference personnalisé, qui contient la disposition de la boîte de dialogue. Ajoutez ce DialogPreference à votre fichier de préférences XML.

0
Mr-IDE

L'exception se produit lorsque votre FontPreferenceFragment n'implémente pas DialogPreference.TargetFragment . Vous devrez vous assurer que votre fragment implémente cette interface.

0
ianhanniballake