Первый показанный фрагмент ViewPager всегда неверен с FragmentStatePager

Я пытаюсь создать тот же view pager + tabs дизайн, что и PlayStore 5.1.x. Вот мой макет:

<?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. Теперь все работает хорошо!

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

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


person S.Thiongane    schedule 27.12.2014    source источник
comment
Избавьтесь от private ArrayList<FakeFragment> fragments и просто getItem() верните новый экземпляр фрагмента. Полная и цельная точка позади FragmentStatePagerAdapter состоит в том, чтобы НЕ удерживать все фрагменты в памяти. Если это то, что вы хотите, то все равно избавьтесь от ArrayList<FakeFragment> и переключите адаптер на FragmentPagerAdapter, а не на FragmentStatePagerAdapter. Также избавьтесь от getItemPosition(). FWIW, вот ряд примеров ViewPagerиспользующих приложений.   -  person CommonsWare    schedule 01.01.2015
comment
Спасибо @CommonsWare. Я выбрал FragmentStatePagerAdapter, потому что у меня есть 6 страниц в моем ViewPager, на каждой странице у меня есть GridView с большим количеством данных для отображения. Вот почему я хотел использовать этот тип адаптера, чтобы воссоздать страницу или обновить ее содержимое только в случае необходимости. Вот почему я также пытался переопределить getItemPosition. Возможно ли это с FragmentPagerAdapter ? еще раз спасибо за ответ!   -  person S.Thiongane    schedule 01.01.2015
comment
Ваша реализация должна работать корректно в обоих случаях, разница может заключаться только в скорости. Если загрузка данных требует больших затрат и вам не нужно делать это каждый раз при использовании FragmentStatePagerAdapter, используйте Fragment.onSaveInstanceState для сохранения загруженного набора данных.   -  person Eugen Pechanec    schedule 01.01.2015
comment
спасибо @EugenPechanec, я постараюсь сделать то, что вы предложили.   -  person S.Thiongane    schedule 01.01.2015
comment
Я решил проблему, но все еще не понимаю ее хорошо. Пожалуйста, посмотрите мое редактирование и помогите мне понять это! заранее спасибо.   -  person S.Thiongane    schedule 08.01.2015
comment
@CommonsWare, Евгений ... нужна ваша помощь в последний раз :)   -  person S.Thiongane    schedule 08.01.2015
comment
У меня такая же проблема даже сегодня с очень похожим кодом. Массив имеет [a, b, c, d]... это должен быть порядок страниц. Но мои страницы появляются впервые как [b, b, c, d]. При пролистывании назад c, b, a ИЛИ d, c, b, a. Журналы показывают, что 1-й адаптер получает значение a и позицию 0, но «a» никогда не отображается. Все еще интересно, что может быть не так.   -  person lini sax    schedule 21.06.2015
comment
Пожалуйста, посмотрите мой анализ и то, как я понял проблему stackoverflow.com/a/30968767/816635   -  person lini sax    schedule 21.06.2015


Ответы (2)


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

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

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

person Eugen Pechanec    schedule 01.01.2015
comment
Спасибо, @Eugen, не могли бы вы дать больше информации о 3): the adapter should not depend on the parent class to get its data set? - person S.Thiongane; 07.01.2015
comment
Лучше всего объявлять вложенные классы static. Это удаляет их неявную связь с их родительским классом (обратите внимание, что вы не можете получить доступ к переменным родительского поля). В некоторых случаях это может предотвратить утечку памяти. Используйте нестатические внутренние классы только в том случае, если вы знаете, что делаете. Попробуйте прочитать это. - person Eugen Pechanec; 07.01.2015
comment
Я принял ваш ответ и дал вам награду, потому что ваш ответ и комментарий привели меня к другой полезной информации. - person S.Thiongane; 08.01.2015
comment
Я рад, что смог помочь. Удачи с вашим приложением. - person Eugen Pechanec; 08.01.2015
comment
см. сообщение stackoverflow.com/questions/33012056/ - person kartheeki j; 08.10.2015

Реализация getItem() является проблемой.

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

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

person shkschneider    schedule 06.01.2015