Czy PreferenceFragment został celowo wykluczony z pakietu zgodności?

153

Chcę napisać preferencje, które można zastosować zarówno do urządzeń 3.0, jak i starszych. Odkrywając, że PreferenceActivityzawiera przestarzałe metody (chociaż są one używane w towarzyszącym przykładowym kodzie), przyjrzałem się PreferenceFragementi pakietowi zgodności, aby rozwiązać moje problemy.

Wydaje się jednak, że PreferenceFragmentnie ma tego w pakiecie zgodności. Czy ktoś może mi powiedzieć, czy to było zamierzone? Jeśli tak, czy mogę łatwo wybrać zakres urządzeń (np. <3.0 i> = 3.0), czy też będę musiał przeskakiwać przez przeszkody? Jeśli nie zostało to celowo wykluczone, czy możemy spodziewać się nowej wersji pakietu kompatybilności? A może istnieje inne obejście, które jest bezpieczne w użyciu?

Twoje zdrowie

James

James
źródło
1
Oto moje podejście do rozwiązania problemu: stackoverflow.com/questions/14076073/ ...
ecv
Ktoś stworzył stronę trzecią PreferenceFragment, o której zapomnisz, że nawet tam jest. Zobacz moją odpowiedź .
theblang
Chris Banes odnosi się do tego w komentarzu na swoim blogu . Powiedział, że powodem jest"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang
Zobacz moją zaktualizowaną odpowiedź . PreferenceFragmentCompatzostał niedawno dodany do biblioteki wsparcia.
theblang

Odpowiedzi:

90

Odkrywanie, że PreferenceActivity zawiera przestarzałe metody (chociaż są one używane w towarzyszącym przykładowym kodzie)

Przestarzałe metody są przestarzałe w systemie Android 3.0. Działają doskonale na wszystkich wersjach Androida, ale kierunek jest taki, aby używać PreferenceFragmentna Androidzie 3.0 i nowszych.

Czy ktoś może mi powiedzieć, czy to było zamierzone?

Domyślam się, że jest to kwestia czasu inżynieryjnego, ale to tylko przypuszczenie.

Jeśli tak, czy mogę łatwo wybrać zakres urządzeń (np. <3.0 i> = 3.0), czy też będę musiał przeskakiwać przez przeszkody?

Uważam to za „łatwe”. Miej dwie oddzielne PreferenceActivityimplementacje, jedną używającą nagłówków preferencji PreferenceFragments, a drugą używającą oryginalnego podejścia. Wybierz właściwy w momencie, w którym chcesz (np. Gdy użytkownik kliknie element menu opcji). Oto przykładowy projekt demonstrujący to. Lub mieć pojedynczy, PreferenceActivityktóry obsługuje oba przypadki, jak w tym przykładowym projekcie .

Jeśli nie zostało to celowo wykluczone, czy możemy spodziewać się nowej wersji pakietu kompatybilności?

Dowiesz się, kiedy reszta z nas się dowie, to znaczy, czy i kiedy zostanie wysłany.

A może istnieje inne obejście, które jest bezpieczne w użyciu?

Patrz wyżej.

CommonsWare
źródło
Pozdrawiam. Widziałem, że skomentowałeś to w kilku miejscach (grupa google android i twój blog), ale chciałeś ostatecznej odpowiedzi (o ile można uzyskać, biorąc pod uwagę okoliczności).
James
@James: Tak, rub będzie w definicji XML preferencji, otrzymując coś, co będzie działać dobrze jako fragmenty, a także połączone razem, ponieważ nie jestem pewien, czy <include>działa z XML preferencji. Przy okazji, jeśli jesteś subskrybentem, aktualizacja książki odnosząca się do tego projektu została ogłoszona kilka minut temu.
CommonsWare
7
Przepraszam, ale nie bardzo wiem, o co ci chodzi. W ogóle nie odpowiadasz, a jedynie komentujesz / zgadujesz / odsyłasz do nieistotnych linków zewnętrznych, które nie mają nic wspólnego z problemem. Pytanie brzmi, czy pominięcie było zamierzone, czy nie, bez kompatybilnej wersji PreferenceFragment nie ma możliwości rozszerzenia PreferenceActivity w sposób, który opisałeś, ponieważ jeśli PreferenceFragment nie istnieje, to nie ma metody getSupportFragmentManager () ani żadnej innej metody wymagane jest użycie fragmentów w pierwszej kolejności.
Justin Buser
8
@JustinBuser: „Pytanie brzmi, czy pominięcie było zamierzone” - jedyne osoby, które mogą odpowiedzieć, że praca dla Google. Zapraszamy do podjęcia pracy w Google, aby spróbować się dowiedzieć. „nie ma sposobu, aby rozszerzyć PreferenceActivity w sposób, który opisałeś” - zapraszamy do pobrania kodu, do którego utworzyłem łącze.
CommonsWare
9
@JustinBuser Dla przypomnienia, Mark odpowiedział na moje pytanie. To oczywiste, że akceptuję jego odpowiedź.
James
21

Subtelna konsekwencja odpowiedzi z @CommonsWare jest taka, że ​​- Twoja aplikacja musi wybierać między interfejsem API zgodności lub wbudowanym interfejsem API fragmentów (od SDK 11 lub tak dalej). W rzeczywistości to właśnie spowodowało zalecenie „łatwo”. Innymi słowy, jeśli chcesz używać PreferenceFragment, Twoja aplikacja musi korzystać z wbudowanego interfejsu API fragmentów i obsługiwać przestarzałe metody w PreferenceActivity. I odwrotnie, jeśli ważne jest, aby Twoja aplikacja korzystała z kompatybilności. API będziesz mieć do czynienia z brakiem klasy PreferenceFragment. W związku z tym urządzenia celownicze nie stanowią problemu, ale przeskakiwanie ma miejsce, gdy musisz wybrać jeden lub drugi interfejs API, a tym samym poddać projekt nieprzewidzianemu obejściu. Potrzebuję kompatybil. API, więc utworzę własną klasę PreferenceFragment i zobaczę, jak to działa. W najgorszym przypadku I '

EDYCJA: po wypróbowaniu i obejrzeniu kodu na http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java? av = h - tworzenie własnego elementu PreferenceFragment nie nastąpi. Wydaje się, że głównym blokerem jest liberalne użycie prywatnego pakietu w PreferenceManager zamiast „chronionego”. Naprawdę nie wygląda na to, że jest jakieś zabezpieczenie lub naprawdę dobra motywacja, aby to zrobić, i nie jest to dobre do testowania jednostkowego, ale cóż ... mniej pisania chyba ...

EDYCJA v2: Właściwie to się wydarzyło i zadziałało. Z pewnością ból głowy sprawił, że kod działał z plikiem JAR interfejsu Compatibility API. Musiałem skopiować około 70% pakietu com.android.preference z SDK do mojej aplikacji, a następnie zmagać się z typowo przeciętnej jakości kodem Java w systemie Android. Użyłem wersji 14 SDK. Inżynierowi Google byłoby znacznie łatwiej zrobić to, co ja, w przeciwieństwie do tego, co słyszałem od niektórych czołowych inżynierów Androida, którzy mówią na ten temat.

BTW - czy powiedziałem, że „kierowanie na urządzenia to nie problem”? To jest całkowicie ... jeśli używasz com.android.preference, nie będziesz mógł zamienić się z interfejsem Compatibility API bez poważnej refaktoryzacji. Zabawny dziennik!

Wytrwały
źródło
Pozwólcie, że będę bardziej bezpośredni. Jeśli zależy Ci tylko na wycelowaniu w Honeycomb i wyższe (który ma jak duży udział w rynku?), Zagłosuj na odpowiedź od @Commonsware! Jeśli zależy Ci na większości urządzeń z Androidem dostępnych obecnie na rynku, przeczytaj moją odpowiedź.
Wytrwały
4
czy byłbyś skłonny opowiedzieć, jak to zrobiłeś? Biegnę w dokładnie taki sam problem, tylko mój PreferenceActivity musi używać ładowarki i dlatego musi korzystać z biblioteki kompatybilności.
Karakuri
3
@Tenacious Podoba mi się twoje dochodzenie - dobra robota. Uważam jednak, że ktoś powinien ustawić rekord bezpośrednio w twoim pierwszym komentarzu - kod Commonsware będzie działał na urządzeniach przed i po HC - wypróbuj go najpierw, zanim opublikujesz takie komentarze. Musisz zdać sobie sprawę z późnego powiązania używanego w czasie wykonywania do obsługi poprzednich urządzeń. Sprawdzanie wersji w czasie wykonywania dba o obsługę obu rodzin systemu operacyjnego - jest to typowy wzorzec Androida (nie taki, który mi się podoba - ale taki, który jest ważny dla programistów Androida, aby się go nauczyli i zaznajomili) ... Więc przyszłym czytelnikom - nie nie odrzucaj żadnego podejścia.
Richard Le Mesurier
@RichardLeMesurier, ale metoda Commonsware nie jest odpowiednia, jeśli potrzebujesz preferencji w DrawerLayout
neworld
16

Opierając się na odpowiedzi CommonsWare, a także obserwacjach Tenacious ', wymyśliłem rozwiązanie klasy potomnej, zdolne do kierowania wszystkich obecnych wersji Android API przy minimalnym zamieszaniu i bez duplikacji kodu lub zasobów. Zobacz moją odpowiedź na powiązane pytanie tutaj: PreferenceActivity Android 4.0 i starsze

lub na moim blogu: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Przetestowano na dwóch tabletach z systemem 4.0.3 i 4.0.4, a także telefonie z systemem 4.0.4 i 2.3.3 oraz emulatorze z wersją 1.6.

Wujek Code Monkey
źródło
10

W sierpniu 2015 firma Google wydała nową bibliotekę obsługi preferencji v7 .

Teraz możesz użyć PreferenceFragmentCompat z dowolnym ActivitylubAppCompatActivity

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

Musisz ustawić preferenceThemew swoim motywie:

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

W ten sposób możesz dostosować preferenceThemestyl układów używanych dla każdego typu preferencji bez wpływu na inne części działania.

Gabriele Mariotti
źródło
1
Myślę, że tęsknisz za funkcją: onCreatePreferences
programista Androida
To uratowało mi dzień! Zastanawiam się, dlaczego nie jest to widoczne w strukturze projektu Android Studio ... ?? BTW masz literówkę w kodzie. Powinno być „rozszerza PreferenceFragmentCompat”
Grzegorz D.
7

Odpowiedź Wytrwałego jest poprawna, ale oto więcej szczegółów.

Powodem, dla którego nie można „utworzyć normalnego układu i ręcznie powiązać składniki widoku z Sharedprefs” jest fakt, że w API android.preferences występują zaskakujące pominięcia. PreferenceActivity i PreferenceFragment mają dostęp do krytycznych niepublicznych metod PreferenceManager, bez których nie można zaimplementować własnego interfejsu użytkownika preferencji.

W szczególności, aby skonstruować hierarchię preferencji z pliku XML, musisz użyć PreferenceManager, ale wszystkie konstruktory PreferenceManager są albo prywatne, albo ukryte. Metoda dołączania detektorów Preference onClick do twojego działania jest również prywatna dla pakietu.

Nie można tego obejść, podstępnie umieszczając swoją implementację w pakiecie android.preferences, ponieważ niepubliczne metody w Android API są w rzeczywistości pomijane w SDK. Przy odrobinie kreatywności obejmującej refleksję i dynamiczne proxy, nadal możesz się do nich dostać. Jedyną alternatywą, jak mówi Tenacious, jest rozwidlenie całego pakietu android.preference, w tym co najmniej 15 klas, 5 układów i podobną liczbę elementów style.xml i attrs.xml.

Odpowiadając na pierwotne pytanie, powodem, dla którego Google nie uwzględnił PreferenceFragment w pakiecie zgodności, jest to, że mieliby dokładnie taką samą trudność jak Tenacious i ja. Nawet Google nie może cofnąć się w czasie i upublicznić tych metod na starych platformach (chociaż mam nadzieję, że zrobią to w przyszłych wydaniach).

mhsmith
źródło
2

Celem mojej aplikacji jest API +14, ale ze względu na korzystanie z biblioteki wsparcia dla jakiejś wymyślnej nawigacji nie mogłem używać android.app.Fragmenti musiałem używać android.support.v4.app.Fragment, ale musiałem również mieć to PreferenceFragmentmiejsce bez dużych zmian w kodzie.

Więc moja łatwa poprawka do posiadania obu światów biblioteki wsparcia i 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
źródło
2

Musiałem zintegrować Preferencje z projektem aplikacji i zachować wsparcie dla Androida 2.3. Więc nadal potrzebowałem PreferencesFragment.

Po kilku poszukiwaniach znalazłem android-support-v4-preferencefragment lib. Ta biblioteka oszczędza dużo czasu na kopiowanie i refaktoryzację oryginalnego PreferencesFragment, jak powiedział Tenacious. Działa dobrze, a użytkownicy lubią preferencje.

nowy świat
źródło