Czego używać zamiast „addPreferencesFromResource” w PreferenceActivity?

360

Właśnie zauważyłem fakt, że metoda addPreferencesFromResource(int preferencesResId)jest oznaczona jako przestarzała w dokumentacji Androida ( Reference Entry ).

Niestety w opisie metody nie podano żadnej alternatywnej metody.

Której metody należy użyć zamiast tego, aby połączyć plik preferenceScreen.xml z pasującą funkcją PreferenceActivity?

mweisz
źródło
2
Bardzo proste podejście zapewnia WannaGetHigh na stackoverflow.com/questions/23523806/…
Sabin
Tamte rozwiązanie nadal się stosuje addPreferencesFromResource(int preferencesResId). Czy coś brakuje?
Jammo
@Jammo Tak, ale został przeniesiony z działania do fragmentu, aby odzwierciedlić nowy sposób robienia tego -> fragment.
WannaGetHigh

Odpowiedzi:

332

W opisie metody nie podano żadnej metody alternatywnej, ponieważ preferowanym podejściem (od poziomu API 11) jest tworzenie instancji obiektów PreferenceFragment w celu załadowania preferencji z pliku zasobów. Zobacz przykładowy kod tutaj: PreferenceActivity

glorifiedHacker
źródło
Bardzo dziękuję za odpowiedź. Właśnie postępowałem zgodnie z przestarzałymi instrukcjami z „Video2Brain-Android Development”, które doprowadziły mnie do użycia wersji metody PreferenceActivity. Btw: Chciałbym ocenić twoją odpowiedź jako przydatną, gdybym tylko mógł.
mweisz
33
Teraz tylko jeśli PreferenceFragment został zawarty w pakiecie kompatybilności, sensowne byłoby użycie go stackoverflow.com/questions/5501431/...
christoff
1
Ponieważ korzystam z paska
Someone Somewhere
2
Nadal musisz wywołać addPreferencesFromResource (int PreferencesID), jeśli chcesz, aby aplikacja była wstecznie kompatybilna z poziomem interfejsu API wyższym niż 11 (Android 3.0). Ale myślę, że można również uznać te stare urządzenia za przestarzałe.
Einar Sundgren,
5
@EinarSundgren Jestem właścicielem Nexus One, a moja maksymalna dostępna wersja to 2.2: nie zatrzymasz mnie! NIGDY nie będę przestarzały! Mocą Grayskull ... Mam moc!
TechNyquist
186

Aby dodać więcej informacji do poprawnej odpowiedzi powyżej, po przeczytaniu przykładu z Androida-er znalazłem, że możesz łatwo przekonwertować swoją aktywność preferencyjną na fragment preferencji. Jeśli masz następujące działania:

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

Jedyne zmiany, które musisz wprowadzić, to utworzyć wewnętrzną klasę fragmentu, przenieść addPreferencesFromResources()fragment do fragmentu i wywołać fragment z działania, jak poniżej:

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

Mogą istnieć inne subtelności w tworzeniu bardziej złożonych preferencji z fragmentów; jeśli tak, mam nadzieję, że ktoś je tutaj zauważy.

Garret Wilson
źródło
6
Fajnie, dzięki za to. Java i tego rodzaju myślenie jest jak na razie dalekie od mojego małego, miłego świata .net :)
Tom
44
Dlaczego mieliby tracić na wartości addPreferencesFromResources()w ramach PreferenceFragment? Z początkującego wygląda to niepotrzebnie.
Howdy_McGee
4
Połączenie wymaga interfejsu API na poziomie 11
mehmet
1
więc co jeśli interfejs API jest na poziomie 9? @mehmet
gumuruh
2
Świetna odpowiedź! czy istnieje sposób na osiągnięcie tego bez zmiany zawartości android.R.id.? wydaje mi się nieelegancki z jakiegoś powodu ... (popraw mnie, jeśli się mylę)
tomer.z
37

@Garret Wilson Dziękuję bardzo! Jako noob do kodowania Androida, utknąłem z problemem niezgodności preferencji przez tak wiele godzin i uważam to za tak rozczarowujące, że przestały używać niektórych metod / podejść do nowych, które nie są obsługiwane przez starsze API konieczność korzystania z różnego rodzaju obejść, aby aplikacja działała na wielu różnych urządzeniach. To naprawdę frustrujące!

Twoja klasa jest świetna, ponieważ pozwala ci pracować w nowych interfejsach API z preferencjami, tak jak kiedyś, ale nie jest kompatybilna wstecz. Ponieważ próbuję dotrzeć do szerokiej gamy urządzeń, majstrowałem przy nim trochę, aby działał na urządzeniach w wersji wcześniejszej niż API 11, a także w nowszych interfejsach API:

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

Testy zakończyły się powodzeniem w dwóch emulatorach (2.2 i 4.2).

Dlaczego mój kod wygląda tak kiepsko:

Nie jestem koderem Androida i nie jestem największym fanem Java.

Aby uniknąć przestarzałego ostrzeżenia i zmusić Eclipse do umożliwienia mi kompilacji, musiałem uciekać się do adnotacji, ale wydają się one wpływać tylko na klasy lub metody, więc musiałem przenieść kod na dwie nowe metody, aby z tego skorzystać.

Nie chciałbym pisać mojego identyfikatora zasobu xml dwa razy za każdym razem, gdy kopiuję i wklejam klasę dla nowej PreferenceActivity, więc utworzyłem nową zmienną do przechowywania tej wartości.

Mam nadzieję, że przyda się to komuś innemu.

PS: Przepraszam za moje opinie, ale kiedy przychodzisz nowy i odnajdujesz takie utrudnienia, nie możesz się powstrzymać od frustracji!

ecv
źródło
No cóż ... Właśnie zauważyłem, że będę musiał dwukrotnie zmienić nazwę klasy zewnętrznej za każdym razem, gdy ją kopiuję i wklejam. W rzeczywistości można tego uniknąć, przekazując prefiksy do klasy wewnętrznej. Nie można utworzyć konstruktora klasy wewnętrznej akceptującego prefs jako parametr, ponieważ najwyraźniej nie jest to zalecane w przypadku klas pochodnych PreferenceFragment. Ponadto nie możesz utworzyć metody w klasie wewnętrznej, aby uzyskać prefs i wywołać addPreferencesFromResource od razu, ponieważ addPreferencesFromResource musi zostać wywołany po wywołaniu super.onCreate, a onCreate nie zostanie wywołany zaraz po wywołaniu klasy pochodnej PreferenceFragment ...
ekw.
... zostały utworzone. Musisz więc utworzyć nową zmienną w klasie wewnętrznej, utworzyć dla niej metodę zbioru publicznego, pozostawić addPreferencesFromResource tam, gdzie jest po wywołaniu super.onCreate, wewnątrz onCreate, utworzyć instancję klasy wewnętrznej, ustawić prefs i użyj go w wywołaniu getFragmentManager () ... jak poprzednio.
ekw.
2
Twój kod był ratownikiem !! Próbowałem celować zarówno w stary, jak i nowy telefon, i zmarnowałem 3 dni .. Odpowiedź jest taka prosta. Dzięki
Devdatta Tengshe,
22

Moje podejście jest bardzo podobne do podejścia Garreta Wilsona (dzięki, głosowałem za;)

Ponadto zapewnia kompatybilność w dół z systemem Android <3.

Właśnie zrozumiałem, że moje rozwiązanie jest jeszcze bliższe rozwiązaniu Kevina Remo . Jest po prostu odrobinę czystszy (ponieważ nie opiera się na antipatternu „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();
    }
}

Dla „prawdziwego” (ale bardziej złożonego) przykładu zobacz NusicPreferencesActivity i NusicPreferencesFragment .

schnatterer
źródło
@SuppressLint("NewApi")- unikaj - bądź bardziej precyzyjny. Czy uruchomiłeś go dla niskich apis? Będzie rzucaćVerifyError
Mr_and_Mrs_D
Testuję powyższe na emulatorze z uruchomionym interfejsem API na poziomie 10, pakiet APK został zbudowany przy użyciu SDK w wersji 19. Jakiej wersji zestawu SDK użyłeś do zbudowania? Na jakim poziomie interfejsu API urządzenia go uruchomiłeś? Czy uruchomiłeś go na emulatorze lub urządzeniu fizycznym? Kiedy dokładnie wystąpił błąd? Jeśli Ur buduje z SDK <= 16, zobacz tę odpowiedź .
schnatterer
Brakuje Ci sensu - i dodaj @Mr_i_Mrs_D, jeśli chcesz, żebym został powiadomiony. Dla VE przeczytać tutaj: stackoverflow.com/questions/20271593/... . To @SuppressLint("NewApi")po prostu zły styl
Mr_and_Mrs_D
@M A więc, co powiesz na propozycję, jak tego uniknąć @SuppressLint("NewApi")w tej konkretnej sytuacji?
schnatterer
@TargetApi(Build.VERSION_CODES.HONEYCOMB)- nie wszystkie ostrzeżenia dla dowolnego interfejsu API :)
Mr_and_Mrs_D
6

Zamiast wyjątków po prostu użyj:

if (Build.VERSION.SDK_INT >= 11)

I użyć

@SuppressLint("NewApi")

tłumić ostrzeżenia.

Piotr
źródło
1
ponieważ nie robi nic innego niż katastrofa: D
Ishtiaq
1
jeśli to katastrofa ... to spróbuj go złapać: D
gumuruh
0

Zamiast używać a PreferenceActivitydo bezpośredniego wczytywania preferencji, użyj AppCompatActivitylub równoważnego, który ładuje aPreferenceFragmentCompat który ładuje preferencje. Jest częścią biblioteki wsparcia (obecnie Android Jetpack) i zapewnia zgodność z API 14.

W swoim build.gradledodaj zależność dla biblioteki obsługi preferencji:

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

Uwaga: założymy, że masz już utworzone preferencje XML.

Dla swojej aktywności utwórz nową klasę aktywności. Jeśli używasz motywów materiałowych, powinieneś przedłużyć AppCompatActivity, ale możesz być elastyczny dzięki temu:

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

Teraz ważna część: utwórz fragment, który ładuje twoje preferencje z XML:

public class MyPreferencesFragment extends PreferenceFragmentCompat {

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

Aby uzyskać więcej informacji, przeczytaj dokumentację dla programistów Androida dla PreferenceFragmentCompat.

Willie Chalmers III
źródło