PreferenceFragment è stato intenzionalmente escluso dal pacchetto di compatibilità?


153

Sto cercando di scrivere le preferenze che possono essere applicate a entrambi i dispositivi 3.0 e pre-3.0. Scoprendo che PreferenceActivitycontiene metodi deprecati (anche se questi sono utilizzati nel codice di esempio allegato), ho esaminato PreferenceFragementil pacchetto di compatibilità per risolvere i miei problemi.

Sembra, tuttavia, che PreferenceFragmentnon sia nel pacchetto di compatibilità. Qualcuno può dirmi se questo era intenzionale? In tal caso, posso facilmente scegliere come target una gamma di dispositivi (ovvero <3.0 e> = 3.0) o dovrò saltare attraverso i cerchi? Se non fosse stato intenzionalmente escluso, possiamo aspettarci una nuova versione del pacchetto di compatibilità? O c'è un'altra soluzione alternativa che è sicura da usare?

Saluti

Giacomo


1
Questo è il mio approccio per risolvere il problema: stackoverflow.com/questions/14076073/...
ECV

Qualcuno ha creato una terza parte PreferenceFragmentche dimenticherai anche lì. Vedere la mia risposta .
theblang,

Chris Banes lo affronta in un commento sul suo blog . Ha detto che la ragione è:"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang,

Vedi la mia risposta aggiornata . PreferenceFragmentCompatè stato recentemente aggiunto alla libreria di supporto.
theblang,

Risposte:


90

Scoprire che PreferenceActivity contiene metodi obsoleti (sebbene questi siano usati nel codice di esempio allegato)

I metodi obsoleti sono obsoleti a partire da Android 3.0. Stanno perfettamente bene su tutte le versioni di Android, ma la direzione è quella di utilizzare PreferenceFragmentsu Android 3.0 e versioni successive.

Qualcuno può dirmi se questo era intenzionale?

La mia ipotesi è che si tratti di tempo di ingegneria, ma è solo una supposizione.

In tal caso, posso facilmente scegliere come target una gamma di dispositivi (ovvero <3.0 e> = 3.0) o dovrò saltare attraverso i cerchi?

Ritengo che sia fatto "facilmente". Hanno due PreferenceActivityimplementazioni separate , una che utilizza le intestazioni delle preferenze e PreferenceFragmentsl'altra che utilizza l'approccio originale. Scegli quello giusto nel punto in cui è necessario (ad esempio, quando l'utente fa clic sulla voce di menu delle opzioni). Ecco un esempio di progetto che lo dimostra. Oppure, avere un singolo PreferenceActivityche gestisca entrambi i casi, come in questo progetto di esempio .

Se non è stato intenzionalmente escluso, possiamo aspettarci una nuova versione del pacchetto di compatibilità?

Scoprirai quando il resto di noi lo scoprirà, vale a dire se e quando spedirà.

O c'è un'altra soluzione alternativa che è sicura da usare?

Vedi sopra.


Saluti Mark. Avevo visto che avevi fatto commenti su questo in un paio di posti (gruppo google android e il tuo blog) ma volevi una risposta definitiva (per quanto si possa ottenere date le circostanze).
James,

@James: Sì, il problema sarà nella definizione XML delle preferenze, ottenendo qualcosa che funzionerà bene come frammenti e anche concatenato insieme, dal momento che non sono sicuro che funzioni <include>con l'XML delle preferenze. A proposito, se sei un abbonato, l'aggiornamento del libro che fa riferimento a questo progetto è stato annunciato pochi minuti fa.
Commons War

7
Mi dispiace ma non sono sicuro di quale punto stai cercando di chiarire qui. Non rispondi affatto, commenta / indovina / fai riferimento a collegamenti esterni non pertinenti che non hanno nulla a che fare con il problema. La domanda è se l'omissione fosse intenzionale o meno, senza la versione di compatibilità di PreferenceFragment non esiste alcun modo per estendere PreferenceActivity nel modo in cui è stato descritto perché se PreferenceFragment non esiste, allora getSupportFragmentManager () o uno qualsiasi degli altri metodi richiesto di utilizzare i frammenti in primo luogo.
Justin Buser,

8
@JustinBuser: "La domanda è se l'omissione era intenzionale" - le uniche persone che possono rispondere a quel lavoro per Google. Ti invitiamo a trovare un lavoro su Google per provare a scoprirlo. "non esiste alcun modo per estendere PreferenceActivity nel modo in cui l'hai descritta" - sei invitato a scaricare il codice a cui mi sono collegato.
Commons War

9
@JustinBuser Per la cronaca, Mark ha risposto alla mia domanda. È evidente da parte mia accettare la sua risposta.
James,

21

La sottile conseguenza della risposta di @CommonsWare è che la tua app deve scegliere tra l'API di compatibilità o l'API del frammento integrata (dal SDK 11 in poi). In effetti è quello che ha fatto la raccomandazione "facilmente". In altre parole, se si desidera utilizzare PreferenceFragment, l'app deve utilizzare l'API del frammento integrata e gestire i metodi obsoleti su PreferenceActivity. Al contrario, se è importante che la tua app usi la compatibilità. API che dovrai affrontare senza avere una classe PreferenceFragment. Pertanto, il targeting dei dispositivi non è un problema, ma il salto del cerchio si verifica quando devi scegliere l'una o l'altra API e quindi inviare il tuo progetto a soluzioni impreviste. Ho bisogno del compat. API, quindi ho intenzione di creare la mia classe PreferenceFragment e vedere come funziona. Nel peggiore dei casi io '

EDIT: dopo aver provato e guardando il codice su http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java? av = h - la creazione del mio PreferenceFragment non accadrà. Sembra che l'uso principale di package-private in PreferenceManager invece di "protetto" sia il principale blocco. Non sembra che ci sia alcuna sicurezza o una buona motivazione per averlo fatto e non è eccezionale per i test unitari, ma vabbè ... meno battitura a macchina immagino ...

EDIT v2: in realtà è successo e ha funzionato. È stato sicuramente un mal di testa far funzionare il codice con il JAR API di compatibilità. Ho dovuto copiare circa il 70% del pacchetto com.android.preference dall'SDK alla mia app e quindi lottare con un codice Java di qualità mediocre in Android. Ho usato la v14 dell'SDK. Sarebbe stato molto più facile per un ingegnere Goog fare ciò che ho fatto, contrariamente a quanto ho sentito dire da alcuni ingegneri Android leader su questo argomento.

A proposito, ho detto "il targeting dei dispositivi non è un problema"? È totalmente ... se usi com.android.preference non sarai in grado di scambiare con l'API di compatibilità senza importanti refactoring. Registro divertente!


Vorrei essere più diretto. Se tutto ciò che ti interessa è il targeting Honeycomb e superiore (che ha quanta quota di mercato?), Allora vota per la risposta da @Commonsware! Se ti interessa la maggior parte dei dispositivi Android oggi sul mercato, dovresti leggere la mia risposta.
Tenace

4
saresti disposto a condividere come hai fatto? Sto correndo nella esattamente lo stesso problema, solo la mia PreferenceActivity deve usare Caricatori e quindi mi devo utilizzare la libreria di compatibilità.
Karakuri,

3
@Tenace Mi piace la tua indagine - ben fatto. Tuttavia, ritengo che qualcuno dovrebbe impostare il record direttamente sul tuo primo commento lì - il codice Commonsware funzionerà su dispositivi pre e post HC - provalo prima di fare commenti del genere. La cosa che devi realizzare è l'associazione tardiva utilizzata in fase di esecuzione per supportare i dispositivi precedenti. Il controllo della versione in fase di esecuzione si occupa di supportare entrambe le famiglie di sistemi operativi - questo è un modello Android comune (non uno che mi piace - ma che è importante per gli sviluppatori Android per imparare e familiarizzare) ... Quindi per i futuri lettori - don non respingere nessuno dei due approcci.
Richard Le Mesurier,

@RichardLeMesurier ma il metodo Commonsware non è corretto se hai bisogno di preferenze all'interno di DrawerLayout
neworld

16

Basandomi sulla risposta di CommonsWare e sulle osservazioni di Tenacious, ho trovato un'unica soluzione di classe discendente in grado di indirizzare tutte le attuali versioni dell'API Android con il minimo sforzo e senza duplicazioni di codice o risorse. Vedi la mia risposta alla domanda correlata qui: PreferenceActivity Android 4.0 e precedenti

o sul mio blog: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Testato su due tablet con 4.0.3 e 4.0.4, telefono con 4.0.4 e 2.3.3 e un emulatore con 1.6.



10

Nell'agosto 2015 Google ha rilasciato la nuova libreria di supporto per le preferenze v7 .

Ora puoi usare PreferenceFragmentCompat con qualsiasi ActivityoAppCompatActivity

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

Devi impostare il preferenceThemetuo tema:

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

In questo modo è possibile personalizzare lo preferenceThemestile dei layout utilizzati per ciascun tipo di preferenza senza influire su altre parti dell'attività.


1
Penso che ti manchi una funzione: onCreatePreferences
sviluppatore Android il

Questo mi ha salvato la giornata! Mi chiedo perché questo non sia visibile nella struttura del progetto di Android Studio ... ?? A proposito, hai un refuso nel tuo codice. Dovrebbe essere "estende PreferenceFragmentCompat"
Grzegorz D.

7

La risposta di Tenacious è corretta, ma qui ci sono alcuni dettagli in più.

Il motivo per cui non è possibile "creare un layout normale e associare manualmente i componenti della vista a sharedprefs" è che ci sono alcune omissioni sorprendenti nell'API android.preferences. PreferenceActivity e PreferenceFragment hanno entrambi accesso ai metodi critici PreferenceManager non pubblici, senza i quali non è possibile implementare un'interfaccia utente delle proprie preferenze.

In particolare, per costruire una gerarchia delle preferenze da un file XML è necessario utilizzare un PreferenceManager, ma tutti i costruttori di PreferenceManager sono privati ​​o nascosti. Anche il metodo di allegare i listener di Preference onClick alla tua attività è privato del pacchetto.

E non puoi aggirare questo problema inserendo di nascosto la tua implementazione nel pacchetto android.preferences, perché i metodi non pubblici nelle API Android sono effettivamente omessi dall'SDK. Con un po 'di creatività che coinvolge riflessione e proxy dinamici, puoi ancora raggiungerli. L'unica alternativa, come dice Tenacious, è di fork l'intero pacchetto android.preference, che include almeno 15 classi, 5 layout e un numero simile di elementi style.xml e attrs.xml.

Quindi, per rispondere alla domanda originale, il motivo per cui Google non ha incluso PreferenceFragment nel pacchetto di compatibilità è che avrebbero avuto esattamente la stessa difficoltà di Tenacious e di me stesso. Anche Google non può tornare indietro nel tempo e rendere pubblici questi metodi nelle vecchie piattaforme (anche se spero lo facciano nelle versioni future).


2

Il mio target di app è API +14 ma a causa dell'utilizzo della libreria di supporto per una navigazione sofisticata, non ho potuto usare android.app.Fragmente android.support.v4.app.Fragmentdovevo usare , ma dovevo anche averePreferenceFragment a posto senza grandi modifiche al codice dietro.

Quindi la mia soluzione semplice per avere entrambi i mondi della libreria di supporto e 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

Avevo bisogno di integrare le preferenze nella progettazione dell'applicazione e mantenere il supporto per 2.3 Android. Quindi avevo ancora bisogno di PreferencesFragment.

Dopo alcune ricerche ho trovato la libreria android-support-v4-preferenzefragment . Questa lib risparmia un sacco di tempo per la copia e il refactoring del PreferenzeFragment originale come diceva Tenacious. Funziona bene e gli utenti godono delle preferenze.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.