8

Я стараюсь иметь такой же дизайн view pager + tabs, что и в PlayStore 5.1.x. Вот мой макет:Показанный первый фрагмент ViewPager всегда неверен с 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> 

Мой адаптер:

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

Мои закладки и пейджера показываются правильно! но я заметил, что первый фрагмент, показанный в пейджере просмотра, всегда совпадает со вторым. Затем, когда я прокручиваю один раз, дважды и перебираю обратно на первую страницу, я обнаружил, что теперь отображается правильный фрагмент !!

Я не могу понять, почему это поведение, пожалуйста, мне нужны объяснения.

РЕШЕНИЕ

Проблема была из-за мое определение FakeFragment.newInstance() метода.

private static int position; 

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

Я изменил его, используя setArguments(args) моих FakeFragment например, а затем получить его в onCreate методы. Теперь все работает хорошо!

Может кто-нибудь объяснить мне, почему?

Я думаю, что таким образом значение позиции будет зависеть от жизненного цикла фрагмента, так что всегда будет ожидаемой позицией, правильно?

+1

Избавьтесь от 'private ArrayList фрагментов' и ​​просто получив' getItem() 'возвращаемый новый экземпляр фрагмента. ** полная и целая * точка *** позади «FragmentStatePagerAdapter» - это ** NOT ** удерживать все фрагменты в памяти. Если это то, что вы хотите, тогда все равно избавитесь от 'ArrayList 'и переключите ваш адаптер как« FragmentPagerAdapter », а не« FragmentStatePagerAdapter ». Также избавьтесь от 'getItemPosition()'. [FWIW, вот серия примеров «приложений для просмотра ViewPager» (https://github.com/commonsguy/cw-omnibus/tree/master/ViewPager). – CommonsWare

+0

Спасибо @CommonsWare. Я выбрал «FragmentStatePagerAdapter», потому что у меня 6 страниц в моем «ViewPager», на каждой странице у меня есть «GridView» со многими данными для отображения.Вот почему я хотел использовать этот тип адаптера, чтобы воссоздать страницу или обновить ее контент только при необходимости. Вот почему я также попытался переопределить 'getItemPosition'. Возможно ли это с помощью «FragmentPagerAdapter»? Еще раз спасибо за ответ! –

+1

Ваша реализация должна работать правильно в обоих случаях, единственной особенностью может быть скорость. Если загрузка данных стоит дорого, и вам не нужно ее каждый раз использовать 'FragmentStatePagerAdapter', используйте' Fragment.onSaveInstanceState' для хранения загруженного набора данных. –

ответ

6

1) Не используйте getItemPosition(Object), если вы не справитесь с этим. Вы не обязаны реализовывать его, и вы можете сломать некоторые другие функции, внедряя их неправильно.

2) Точка getItem(int) должна возвращать новый фрагмент. Опустите массив фрагментов, поскольку это не имеет никакого смысла.

3) Сделайте класс adpater static (он способствует повторному использованию, адаптер не должен зависеть от родительского класса, чтобы получить его набор данных, правильно?) И передать Category s в качестве параметра конструктора. Храните его в переменной и создавайте новые фрагменты в соответствии с этим набором данных. Вероятно, вы также захотите передать Category[position] в качестве параметра конструктору фрагмента, а не только position.

+0

Спасибо @Eugen, пожалуйста, можете предоставить дополнительную информацию о 3): 'адаптер не должен зависеть от родительского класса, чтобы получить его набор данных? –

+1

Лучше всего объявлять вложенные классы 'static'. Это удаляет их неявное соединение с их родительским классом (обратите внимание, что вы не можете получить доступ к переменным поля родителя). В некоторых случаях это может привести к утечке памяти. Используйте только нестатические внутренние классы, если знаете, что делаете. Попробуйте прочитать [this] (http://stackoverflow.com/a/70358/2444099). –

+0

Я принял ваш ответ и дал вам награды, потому что ваш ответ и комментарий привели меня к другой полезной информации. –

3

Ошибка getItem().

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

Вы никогда не должны изменять данные в этом методе GET: не называйте add() в нем. Я сомневаюсь, что Adapter знал бы, что вы добавили элемент.

Смежные вопросы