8

Próbuję mieć ten sam projekt view pager + tabs jak PlayStore 5.1.x. Oto mój plan:Pierwszy pokazany fragment programu ViewPager zawsze jest nieprawidłowy przy użyciu fragmentu FragmentStatePager

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:layout_gravity="center_vertical|center_horizontal" 
    android:gravity="center_vertical|center_horizontal" 
    android:orientation="vertical"> 

    <com.astuetz.PagerSlidingTabStrip 
     android:id="@+id/tabs" 
     android:layout_width="match_parent" 
     android:layout_height="50dp" 
     android:background="@drawable/background_tabs" /> 

    <android.support.v4.view.ViewPager 
     android:id="@+id/pager" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 

</LinearLayout> 

Moja Adapter:

public class MainPagerAdapter extends FragmentStatePagerAdapter { 

    private ArrayList<FakeFragment> fragments; 

    public MainPagerAdapter(FragmentManager fm) { 
     super(fm); 
     // TODO Auto-generated constructor stub 
     fragments = new ArrayList<FakeFragment>(); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     // TODO Auto-generated method stub  
     if(position < getCount()) { 
      FakeFragment fragment = FakeFragment.newInstance(position); 
      fragments.add(fragment); 
     } 
     return fragments.get(position); 
    } 

    @Override 
    public int getCount() { 
     // TODO Auto-generated method stub 
     return Category.values().length; 
    } 

    @Override 
    public CharSequence getPageTitle(int position) { 
     // TODO Auto-generated method stub 
     return Category.values()[position].getTitle(); 
    } 

    @Override 
    public int getItemPosition(Object object) { 
     // TODO Auto-generated method stub 
     return POSITION_NONE; 
    } 
} 

Moje zakładki i pager są wyświetlane poprawnie! ale zauważyłem, że pierwszy fragment wyświetlany w okienku widokowym jest zawsze taki sam jak drugi. Następnie, gdy przesunę raz, dwa razy i przesunę z powrotem na pierwszą stronę, stwierdzam, że właściwy fragment jest teraz wyświetlany !!

Nie mogę zrozumieć, dlaczego to zachowanie, potrzebuję pewnych wyjaśnień.

ROZWIĄZANIE

Problem był z powodu mojej definicji FakeFragment.newInstance() metody.

private static int position; 

public static FakeFragment newInstance(int position) { 
    // TODO Auto-generated method stub 
    FakeFragment.position = position; 
    return new FakeFragment(); 
} 

Zmieniłem go za pomocą setArguments(args) do mojego instancji FakeFragment, a następnie pobrać ją onCreate metody. Teraz wszystko działa dobrze!

Czy ktoś może mi wyjaśnić, dlaczego?

Myślę, że w ten sposób wartość pozycji będzie zależała od cyklu życia fragmentu, więc zawsze będzie to oczekiwana pozycja, prawda?

+1

Pozbądź się 'private ArrayList i po prostu' getItem() 'zwróć nowe wystąpienie fragmentu. ** ** kompletny i cały * punkt *** za 'FragmentStatePagerAdapter' to ** NOT ** przytrzymaj na wszystkich fragmentach w pamięci. Jeśli tego chcesz, to pozbyć się 'ArrayList ' i zmienić adapter na 'FragmentPagerAdapter', a nie' FragmentStatePagerAdapter'. Również pozbyć się 'getItemPosition()'. [FWIW, oto seria przykładowych aplikacji "ViewPager"] (https://github.com/commonsguy/cw-omnibus/tree/master/ViewPager). – CommonsWare

+0

Dzięki @CommonsWare. Wybrałem 'FragmentStatePagerAdapter', ponieważ mam 6 stron w moim' ViewPager', na każdej stronie mam 'GridView' z wieloma danymi do pokazania.Właśnie dlatego chciałem użyć tego rodzaju adaptera, aby odtworzyć stronę lub odświeżyć jej zawartość tylko w razie potrzeby. Dlatego też próbowałem przesłonić 'getItemPosition'. Czy jest to możliwe z 'FragmentPagerAdapter'? Jeszcze raz dziękuję za odpowiedź! –

+1

Twoja implementacja powinna działać poprawnie w obu przypadkach, jedyną różnicą może być szybkość. Jeśli ładowanie danych jest drogie i nie musisz tego robić za każdym razem, użyj 'FragmentStatePagerAdapter', użyj' Fragment.onSaveInstanceState', aby zapisać załadowany zestaw danych. –

Odpowiedz

6

1) Nie należy implementować getItemPosition(Object), jeśli użytkownik go nie obsługuje. Nie musisz go implementować, możesz też złamać inne funkcje, wprowadzając je nieprawidłowo.

2) Punkt getItem(int) polega na zwrocie nowego fragmentu. Porzuć tablicę fragmentów, ponieważ nie ma sensu.

3) Utwórz klasę zgodności static (Promuje ona możliwość ponownego użycia, adapter nie powinien zależeć od klasy nadrzędnej, aby uzyskać zestaw danych, prawda?) I przekazać Category s jako parametr konstruktora. Zapisz go w zmiennej i utwórz nowe fragmenty zgodnie z tym zbiorem danych. Prawdopodobnie będziesz również chciał przekazać Category[position] jako parametr konstruktorowi fragmentów zamiast tylko position.

+0

Dzięki @Eugen, proszę dać swoje informacje o 3): 'adapter nie powinien zależeć od klasy nadrzędnej, aby uzyskać jego zestaw danych "? –

+1

Najlepiej jest zadeklarować zagnieżdżone klasy 'static'. Spowoduje to usunięcie niejawnego połączenia z ich macierzystą klasą (zauważ, że nie można uzyskać dostępu do zmiennych pola macierzystego). W niektórych przypadkach może to zapobiec wyciekom pamięci. Korzystaj tylko z niestatycznych klas wewnętrznych, jeśli wiesz, co robisz. Spróbuj przeczytać [this] (http://stackoverflow.com/a/70358/2444099). –

+0

Zaakceptowałem twoją odpowiedź i dałem ci nagrody, ponieważ twoja odpowiedź i komentarz doprowadziły mnie do innych przydatnych informacji. –

3

Implementacja getItem() jest problemem.

@Override 
public Fragment getItem(final int position) { 
    return FakeFragment.newInstance(position); 
} 

Nigdy nie należy zmieniać dane w ten sposób otrzymujemy: nie nazywaj add() w nim. Wątpię, czy Adapter wiedziałaby w tym miejscu, że dodałeś element.

Powiązane problemy