Wurde PreferenceFragment absichtlich aus dem Kompatibilitätspaket ausgeschlossen?

153

Ich möchte Einstellungen schreiben, die sowohl auf 3.0- als auch auf Pre-3.0-Geräte angewendet werden können. Als PreferenceActivityich herausfand, dass veraltete Methoden enthalten sind (obwohl diese im beiliegenden Beispielcode verwendet werden), habe ich mir PreferenceFragementdas Kompatibilitätspaket angesehen, um meine Probleme zu lösen.

Es scheint jedoch, dass dies PreferenceFragmentnicht im Kompatibilitätspaket enthalten ist. Kann mir jemand sagen, ob dies beabsichtigt war? Wenn ja, kann ich problemlos auf eine Reihe von Geräten abzielen (z. B. <3.0 und> = 3.0) oder muss ich durch Reifen springen? Können wir eine neue Version des Kompatibilitätspakets erwarten, wenn dies nicht absichtlich ausgeschlossen wurde? Oder gibt es eine andere Problemumgehung, die sicher zu verwenden ist?

Prost

James

James
quelle
1
Dies ist mein Ansatz zur Lösung des Problems: stackoverflow.com/questions/14076073/…
ecv
Jemand hat eine dritte Partei gemacht PreferenceFragment, die Sie vergessen werden, ist sogar da. Siehe meine Antwort .
Theblang
Chris Banes spricht dies in einem Kommentar in seinem Blog an . Er sagte, dass der Grund ist,"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang
Siehe meine aktualisierte Antwort . PreferenceFragmentCompatwurde kürzlich zur Support-Bibliothek hinzugefügt.
Theblang

Antworten:

90

Feststellen, dass PreferenceActivity veraltete Methoden enthält (obwohl diese im zugehörigen Beispielcode verwendet werden)

Die veralteten Methoden sind ab Android 3.0 veraltet. Sie sind auf allen Android-Versionen vollkommen in Ordnung, aber die Richtung ist, PreferenceFragmentauf Android 3.0 und höher zu verwenden.

Kann mir jemand sagen, ob dies beabsichtigt war?

Ich vermute, es ist eine Frage der Engineering-Zeit, aber das ist nur eine Vermutung.

Wenn ja, kann ich problemlos auf eine Reihe von Geräten abzielen (z. B. <3.0 und> = 3.0) oder muss ich durch Reifen springen?

Ich halte es für "einfach". Haben Sie zwei separate PreferenceActivityImplementierungen, eine mit Präferenz-Headern und PreferenceFragmentsdie andere mit dem ursprünglichen Ansatz. Wählen Sie an der gewünschten Stelle die richtige aus (z. B. wenn der Benutzer auf den Menüpunkt Optionen klickt). Hier ist ein Beispielprojekt, das dies demonstriert. Oder haben Sie eine Single PreferenceActivity, die beide Fälle behandelt, wie in diesem Beispielprojekt .

Können wir eine neue Version des Kompatibilitätspakets erwarten, wenn dies nicht absichtlich ausgeschlossen wurde?

Sie werden herausfinden, wann der Rest von uns herausfindet, ob und wann es versendet.

Oder gibt es eine andere Problemumgehung, die sicher zu verwenden ist?

Siehe oben.

CommonsWare
quelle
Prost Mark. Ich hatte gesehen, dass Sie an einigen Stellen (Android Google Group und Ihr Blog) Kommentare dazu abgegeben hatten, aber eine endgültige Antwort wollten (soweit man die Umstände beurteilen kann).
James
@James: Ja, das Problem wird in der Präferenz-XML-Definition liegen und etwas erhalten, das gut als Fragmente funktioniert und auch miteinander verkettet ist, da ich nicht sicher bin, ob es <include>mit Präferenz-XML funktioniert. Übrigens, wenn Sie Abonnent sind, wurde das Buch-Update, das auf dieses Projekt verweist, vor Minuten angekündigt.
CommonsWare
7
Es tut mir leid, aber ich bin mir nicht sicher, welchen Punkt Sie hier ansprechen wollen. Sie beantworten überhaupt nichts, kommentieren / raten / verweisen lediglich auf irrelevante externe Links, die nichts mit dem Problem zu tun haben. Die Frage ist, ob das Auslassen beabsichtigt war oder nicht. Ohne die Kompatibilitätsversion von PreferenceFragment gibt es keine Möglichkeit, PreferenceActivity so zu erweitern, wie Sie es beschrieben haben. Wenn PreferenceFragment nicht vorhanden ist, gibt es auch keine getSupportFragmentManager () oder eine der anderen Methoden erforderlich, um Fragmente in erster Linie zu verwenden.
Justin Buser
8
@JustinBuser: "Die Frage ist, ob die Auslassung beabsichtigt war oder nicht" - die einzigen Personen, die diese Arbeit für Google beantworten können. Sie können gerne einen Job bei Google bekommen, um dies herauszufinden. "Es gibt keine Möglichkeit, PreferenceActivity so zu erweitern, wie Sie es beschrieben haben" - Sie können gerne den Code herunterladen, mit dem ich verlinkt habe.
CommonsWare
9
@ JustinBuser Für die Aufzeichnung hat Mark meine Frage beantwortet. Das zeigt sich daran, dass ich seine Antwort akzeptiere.
James
21

Die Antwort von @CommonsWare impliziert auf subtile Weise: Ihre App muss zwischen der Kompatibilitäts-API oder der integrierten Fragment-API (seit SDK 11 oder so) wählen. Genau das hat die "einfache" Empfehlung getan. Mit anderen Worten, wenn Sie PreferenceFragment verwenden möchten, muss Ihre App die integrierte Fragment-API verwenden und sich mit den veralteten Methoden von PreferenceActivity befassen. Umgekehrt, wenn es wichtig ist, dass Ihre App die Kompatibilität verwendet. API, bei der Sie überhaupt keine PreferenceFragment-Klasse haben. Das Targeting von Geräten ist daher kein Problem, aber das Springen erfolgt, wenn Sie die eine oder andere API auswählen und Ihr Design unvorhergesehenen Problemumgehungen unterziehen müssen. Ich brauche den kompatiblen. API, also werde ich meine eigene PreferenceFragment-Klasse erstellen und sehen, wie das funktioniert. Im schlimmsten Fall habe ich '

BEARBEITEN: Nachdem Sie den Code unter http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java ausprobiert und überprüft haben? av = h - das Erstellen eines eigenen PreferenceFragments wird nicht passieren. Es scheint, dass die liberale Verwendung von package-private in PreferenceManager anstelle von 'protected' der Hauptblocker ist. Es sieht wirklich nicht so aus, als ob es irgendeine Sicherheit oder wirklich gute Motivation dafür gibt, und es ist nicht großartig für Unit-Tests, aber na ja ... weniger Tippen, denke ich ...

EDIT v2: Eigentlich ist es passiert und es hat funktioniert. Es war definitiv ein Problem, den Code mit der Kompatibilitäts-API-JAR zum Laufen zu bringen. Ich musste ungefähr 70% des com.android.preference-Pakets aus dem SDK in meine App kopieren und dann mit typisch mittelmäßigem Java-Code in Android ringen. Ich habe v14 des SDK verwendet. Es wäre für einen Goog-Ingenieur viel einfacher gewesen, das zu tun, was ich getan habe, im Gegensatz zu dem, was einige führende Android-Ingenieure zu diesem Thema gesagt haben.

Übrigens - habe ich gesagt, "Targeting-Geräte sind kein Problem"? Es ist völlig ... wenn Sie com.android.preference verwenden, können Sie ohne umfangreiches Refactoring nicht mit der Kompatibilitäts-API austauschen. Fun Log!

Zäh
quelle
Lass mich direkter sein. Wenn Sie sich nur für Honeycomb und höher interessieren (was hat wie viel Marktanteil?), Stimmen Sie für die Antwort von @Commonsware! Wenn Sie sich für die meisten Android-Geräte auf dem heutigen Markt interessieren, sollten Sie meine Antwort lesen.
Hartnäckig
4
Würdest du gerne mitteilen, wie du das gemacht hast? Ich habe genau das gleiche Problem, nur meine PreferenceActivity muss Loader verwenden und daher muss ich die Kompatibilitätsbibliothek verwenden.
Karakuri
3
@Tenacious Ich mag Ihre Untersuchung - gut gemacht. Ich bin jedoch der Meinung, dass jemand den Datensatz direkt auf Ihren ersten Kommentar dort setzen sollte - Commonscodes Code funktioniert auf HC-Geräten vor und nach dem Eingriff - versuchen Sie es zuerst, bevor Sie solche Kommentare abgeben. Was Sie beachten müssen, ist die späte Bindung, die zur Laufzeit verwendet wird, um frühere Geräte zu unterstützen. Die Versionsprüfung zur Laufzeit sorgt dafür, dass beide Betriebssystemfamilien unterstützt werden - dies ist ein gängiges Android-Muster (nicht eines, das ich mag - aber eines, das für Android-Entwickler wichtig ist, um zu lernen und sich damit vertraut zu machen) ... Also für zukünftige Leser - don lehne keinen Ansatz ab.
Richard Le Mesurier
@ RichardLeMesurier, aber die Methode von Commonsware ist nicht richtig, wenn Sie Einstellungen in DrawerLayout
neworld
16

Aufbauend auf der Antwort von CommonsWare sowie den Beobachtungen von Tenacious habe ich eine Lösung für eine einzelne Nachkommenklasse entwickelt, die alle aktuellen Android-API-Versionen mit minimalem Aufwand und ohne Code- oder Ressourcenverdoppelung ansprechen kann. Bitte lesen Sie meine Antwort auf die entsprechende Frage hier: PreferenceActivity Android 4.0 und früher

oder auf meinem Blog: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Getestet auf zwei Tablets mit 4.0.3 und 4.0.4 sowie einem Telefon mit 4.0.4 und 2.3.3 und einem Emulator mit 1.6.

Onkel Code Affe
quelle
10

Siehe PreferenceFragment-Compat von Machinarius. Es war einfach, mit Gradle vorbeizuschauen, und ich vergesse, dass es sogar da ist.

compile 'com.github.machinarius:preferencefragment:0.1.1'

Wichtiges Update: Die neueste Version von hat v7 support libraryjetzt einen nativen PreferenceFragmentCompat .

theblang
quelle
10

Im August 2015 veröffentlichte Google die neue Preference Support Library v7 .

Jetzt können Sie den PreferenceFragmentCompat mit einem beliebigen Activityoder verwendenAppCompatActivity

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

Sie müssen preferenceThemein Ihrem Thema festlegen :

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

Auf diese Weise können Sie preferenceThemedas Layout der für jeden Voreinstellungstyp verwendeten Layouts anpassen , ohne andere Teile Ihrer Aktivität zu beeinflussen.

Gabriele Mariotti
quelle
1
Ich denke, Sie vermissen eine Funktion: onCreatePreferences
Android-Entwickler
Das hat mir den Tag gerettet! Ich frage mich, warum dies in der Projektstruktur von Android Studio nicht sichtbar ist ... ?? Übrigens haben Sie einen Tippfehler in Ihrem Code. Sollte "erweitert PreferenceFragmentCompat" sein
Grzegorz D.
7

Die Antwort von Tenacious ist richtig, aber hier sind einige weitere Details.

Der Grund, warum Sie nicht "ein normales Layout erstellen und die Ansichtskomponenten manuell an die gemeinsam genutzten Einstellungen binden können", ist, dass die android.preferences-API einige überraschende Auslassungen enthält. PreferenceActivity und PreferenceFragment haben beide Zugriff auf wichtige nicht öffentliche PreferenceManager-Methoden, ohne die Sie keine eigene Präferenz-Benutzeroberfläche implementieren können.

Um eine Voreinstellungshierarchie aus einer XML-Datei zu erstellen, müssen Sie einen PreferenceManager verwenden. Alle Konstruktoren von PreferenceManager sind jedoch entweder paketprivat oder ausgeblendet. Die Methode zum Anhängen der Listener "Preference onClick" an Ihre Aktivität ist ebenfalls paketprivat.

Und Sie können dies nicht umgehen, indem Sie Ihre Implementierung heimlich in das android.preferences-Paket einfügen, da nicht öffentliche Methoden in Android-APIs im SDK tatsächlich weggelassen werden. Mit ein wenig Kreativität, die Reflexion und dynamische Proxies beinhaltet, können Sie sie immer noch erreichen. Die einzige Alternative besteht, wie Tenacious sagt, darin, das gesamte android.preference-Paket zu teilen, einschließlich mindestens 15 Klassen, 5 Layouts und einer ähnlichen Anzahl von style.xml- und attrs.xml-Elementen.

Um die ursprüngliche Frage zu beantworten, hat Google PreferenceFragment nicht in das Kompatibilitätspaket aufgenommen, weil sie genau die gleichen Schwierigkeiten hatten wie Tenacious und ich. Selbst Google kann nicht in die Vergangenheit reisen und diese Methoden auf den alten Plattformen veröffentlichen (obwohl ich hoffe, dass sie dies in zukünftigen Versionen tun).

mhsmith
quelle
2

Mein App-Ziel ist API +14, aber aufgrund der Verwendung der Support-Bibliothek für eine ausgefallene Navigation konnte ich die nicht verwenden android.app.Fragmentund musste sie verwenden android.support.v4.app.Fragment, musste sie aber auch habenPreferenceFragment ohne große Änderungen am Code dahinter einsetzen.

Also meine einfache Lösung für beide Welten der Support-Bibliothek und 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
quelle
2

Ich musste die Einstellungen in das Design der Anwendung integrieren und die Unterstützung für 2.3 android beibehalten. Also brauchte ich immer noch PreferencesFragment.

Nach einiger Suche fand ich Android-Support-v4-Preferencefragment lib. Diese Bibliothek spart viel Zeit für das Kopieren und Umgestalten des ursprünglichen PreferencesFragment, wie Tenacious sagte. Funktioniert gut und Benutzer genießen Vorlieben.

neworld
quelle