15

Voglio supportare almeno api 10, voglio essere in grado di definire le mie preferenze in modo gradevole, voglio essere in grado di avere intestazioni (o di mostrare PreferenceScreen s). Sembra che PreferenceActivity, non completamente supportato dalla colorazione AppCompat, non si adatti. Quindi sto cercando di usare AppCompatActivity e PreferenceFragmentCompat.Come posso creare preferenze personalizzate usando la libreria android.support.v7.preference?

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

Ora, voglio creare una preferenza personalizzata che fornirà la scelta di un font. Con PreferenceActivity, si potrebbe semplicemente fare

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); 
    } 
} 

e utilizzare XML come questo per visualizzarlo

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

Ma ora, non c'è onPrepareDialogBuilder in android.support.v7.preference.DialogPreference. Invece, è stato spostato su PreferenceDialogFragmentCompat. Ho trovato poche informazioni su come usare quella cosa, e non sono sicuro di come passare da xml a visualizzarlo. v14 preferenza frammento ha il seguente codice:

public void onDisplayPreferenceDialog(Preference preference) { 
    ... 

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

ho provato sottoclassi android.support.v7.preference.DialogPreference ed avente onDisplayPreferenceDialog utilizzare un pezzo simile di codice per istanziare un manichino FontPreferenceFragment ma non riesce con le seguenti eccezioni.

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

A questo punto io sono già troppo in profondità nel disordine e non voglio scavare ulteriormente. Google non sa nulla di questa eccezione. Ad ogni modo, questo metodo sembra essere eccessivamente complicato. Quindi, qual è il modo migliore per creare preferenze personalizzate usando la libreria android.support.v7.preference?

risposta

28

Nota importante: Attualmente (v23.0.1 della biblioteca v7) ci sono ancora un sacco di tema-problemi con il 'PreferenceThemeOverlay' (vedi this issue). Su Lollipop, ad esempio, si finisce con intestazioni di categoria stile Holo.

Dopo alcune ore frustranti, sono finalmente riuscito a creare una preferenza v7 personalizzata. Creare il proprio Preference sembra essere più difficile di quanto si pensi sia necessario. Quindi assicurati di prendere un po 'di tempo.

All'inizio ci si potrebbe chiedere perché si troverà sia un DialogPreference sia un PreferenceDialogFragmentCompat per ciascun tipo di preferenza. A quanto pare, la prima è la preferenza effettiva, la seconda è la DialogFragment in cui verrà visualizzata la preferenza. Purtroppo, è necessario sottoclasse entrambi di.

Non preoccuparti, non è necessario modificare alcun codice. Hai solo bisogno di spostare alcuni metodi:

  • Tutti i metodi di preferenza-editing (come setTitle() o persist*()) possono essere trovati nella classe DialogPreference.
  • Tutti i metodi di dialogo (-editing) (onBindDialogView(View) & onDialogClosed(boolean)) sono stati spostati su PreferenceDialogFragmentCompat.

Si potrebbe desiderare che la classe esistente estenda il primo, in questo modo non è necessario passare a molto penso. Il completamento automatico dovrebbe aiutarti a trovare i metodi mancanti.

Una volta completati i passaggi precedenti, è giunto il momento di associare queste due classi. Nel tuo file xml, ti riferirai alla parte delle preferenze. Tuttavia, Android non sa ancora quale Fragment deve gonfiare quando la tua preferenza personalizzata deve essere. Pertanto, è necessario eseguire l'override 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); 
} 

e anche le vostre esigenze DialogFragment per gestire la 'chiave':

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

Questo dovrebbe fare il trucco. Se incontri problemi, prova a dare un'occhiata alle sottoclassi esistenti e scopri come risolve Android (in Android Studio: digita il nome di una classe e premi Ctrl + b per vedere la classe decompilata). Spero che sia d'aiuto.

+2

Quasi una soluzione perfetta! Mi ci è voluto un po 'per capire che onDisplayPreferenceDialog doveva essere sovrascritto nel frammento delle preferenze (che eredita PreferenceFragmentCompat). Molte grazie! –

+2

Solo una nota per chi legge questo: se ottieni un ClassCastException in PreferenceDialogFragmentCompat.java:57/58, assicurati che il tuo bundle.putString ("chiave", preference.getKey()); linea dice "chiave" come sopra. In caso contrario, getArguments(). GetString (ARG_KEY) avrà esito negativo con ClassCastException. – MCLLC

+0

Potresti per favore postare un esempio completo se ne hai uno? Sono riuscito a implementare le nuove preferenze di supporto, ma le DialogPreferences sono un incubo. Google non fornisce alcuna documentazione a riguardo. –

0

L'eccezione si verifica quando il FontPreferenceFragment non implementa DialogPreference.TargetFragment. Dovrai assicurarti che il tuo frammento implementa quell'interfaccia.

+0

Implementa 'DialogPreference.TargetFragment'. Ma anche se l'implementazione risolvesse qualcosa, sarebbe comunque una soluzione. La strada giusta deve essere molto più semplice. – squirrel