ViewPager как круговая очередь / упаковка

Я использую ViewPager с FragmentStatePagerAdapter, чтобы разрешить навигацию между некоторыми фрагментами.

Допустим, у меня есть три фрагмента: A, B и C. ViewPager изначально показывает фрагмент A и позволяет переходить к фрагменту B, проводя справа налево, а затем к фрагменту C, проводя еще раз. Это позволяет использовать следующие пути навигации: A <--> B <--> C.

Я хотел бы иметь возможность проводить пальцем слева направо по фрагменту A и иметь ViewPager, показывающий фрагмент C, т.е. чтобы он вел себя как круговая очередь и позволял ... --> C <--> A <--> B <--> C <--> A <-- ...

Я не хочу, чтобы фрагменты дублировались в других позициях (т. Е. Заканчивались более чем тремя экземплярами).

Возможна ли эта функция упаковки с помощью ViewPager?


person antonyt    schedule 25.09.2011    source источник
comment
Он не встроен в ViewPager, но вы можете попробовать изменить FragmentStatePagerAdapter.instantiateItem, чтобы использовать некоторую математику модуля для загрузки правильного фрагмента. Вам нужно будет переопределить getCount, чтобы вернуть достаточно большое число.   -  person tad    schedule 03.10.2011
comment
@antonyt Удалось ли вам найти решение?   -  person Bryan Denny    schedule 08.10.2011
comment
@BryanDenny еще нет, я фактически перевел свое приложение на другой дизайн. Я отправлю ответ, если получу рабочее решение исходной проблемы.   -  person antonyt    schedule 10.10.2011
comment
Вы нашли решение?   -  person Sudarshan Bhat    schedule 08.02.2012
comment
@Enigma, пожалуйста, посмотрите мой ответ ниже.   -  person antonyt    schedule 11.03.2012
comment
@BryanDenny ты тоже! [может только @ -сообщить одному человеку за сообщение]   -  person antonyt    schedule 11.03.2012
comment
Вы также можете ознакомиться с stackoverflow.com/questions/8239056/ Я опубликовал ответ, для которого требуется всего 2 фиктивные страницы.   -  person Arash    schedule 07.06.2012
comment
Жаль, что ViewPager.mCurPos является частным ...   -  person dcow    schedule 03.04.2013


Ответы (13)


Я реализовал ViewPager / PagerAdapter, который может допускать псевдобесконечное разбиение на страницы. Он работает, указывая очень большое число в качестве фактического количества, но сопоставляет их с фактическим диапазоном набора данных / набора страниц. Он также смещает начало на большое число, так что вы можете сразу же прокрутить влево от «первой» страницы.

Когда вы перейдете на 1000000-ю страницу, это не будет работать так хорошо (вы увидите графические сбои при прокрутке), но, как правило, это не реальный вариант использования. Я мог бы исправить это, время от времени сбрасывая счетчик на меньшее значение, но пока я оставлю все как есть.

InfinitePagerAdapter обертывает существующий ViewPager, поэтому его использование довольно прозрачно. InfiniteViewPager делает небольшую работу, чтобы убедиться, что вы потенциально можете прокручивать влево и вправо много раз.

https://github.com/antonyt/InfiniteViewPager

person antonyt    schedule 11.03.2012
comment
Вау, это здорово. Я превратил его в псевдобесконечный (на самом деле 365) пейджер. Сейчас тестирую с TitlePageIndicator. Большое спасибо - person Bandreid; 28.03.2012
comment
Потрясающе .. Лол! Почему я не подумал об этом ... Однозначно +1 - person Rasmus; 23.07.2012
comment
Проблема возникает, когда количество страниц меньше 4. - person Bobs; 25.08.2012
comment
Здравствуйте, антонит. Как исправить менее 4 фрагментов? Я видел здесь ваш ответ github.com/antonyt/InfiniteViewPager/issues/2, не могли бы вы Помоги мне? Мне нужно поменять местами 3 фрагмента, но я не смог исправить эту ошибку. Как заблокировать destroyItem () / вызов instantiateItem ()? - person DavyJonesUA; 06.03.2013
comment
java.lang.IllegalStateException: фрагмент уже добавлен: я получил это исключение. Я использую менее 3 страниц. - person Arul Pandian; 12.04.2014
comment
Привет, Антонит, Как реализовать индикатор титульной страницы с помощью infinatePageadapter, пожалуйста, помогите мне? - person ranjith; 23.10.2014
comment
@Bandreid, вы реализовали с ним индикатор титульной страницы ... не могли бы вы помочь мне ... поделившись своим опытом? - person ranjith; 23.10.2014

Это решение без поддельных страниц и работает как шарм:

public class CircularViewPagerHandler implements ViewPager.OnPageChangeListener {
    private ViewPager   mViewPager;
    private int         mCurrentPosition;
    private int         mScrollState;

    public CircularViewPagerHandler(final ViewPager viewPager) {
        mViewPager = viewPager;
    }

    @Override
    public void onPageSelected(final int position) {
        mCurrentPosition = position;
    }

    @Override
    public void onPageScrollStateChanged(final int state) {
        handleScrollState(state);
        mScrollState = state;
    }

    private void handleScrollState(final int state) {
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            setNextItemIfNeeded();
        }
    }

    private void setNextItemIfNeeded() {
        if (!isScrollStateSettling()) {
            handleSetNextItem();
        }
    }

    private boolean isScrollStateSettling() {
        return mScrollState == ViewPager.SCROLL_STATE_SETTLING;
    }

    private void handleSetNextItem() {
        final int lastPosition = mViewPager.getAdapter().getCount() - 1;
        if(mCurrentPosition == 0) {
            mViewPager.setCurrentItem(lastPosition, false);
        } else if(mCurrentPosition == lastPosition) {
            mViewPager.setCurrentItem(0, false);
        }
    }

    @Override
    public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
    }
}

Вам просто нужно установить его на свой ViewPager как onPageChangeListener, и все: (** теперь не рекомендуется ** проверьте примечания редактирования)

viewPager.setOnPageChangeListener(new CircularViewPagerHandler(viewPager));

Чтобы избежать этого синего сияния в «конце» вашего ViewPager, вы должны применить эту строку к вашему xml, где находится ViewPager:

android:overScrollMode="never"

Мы улучшили указанное выше решение и создали небольшую библиотеку на github. Не стесняйтесь проверить это :)

Edit :: Согласно комментарию @Bhavana, просто используйте addOnPageChangeListener вместо setOnPageChangeListener, поскольку позже он устарел.

 viewPager.addOnPageChangeListener(new CircularViewPagerHandler(viewPager));
person King of Masses    schedule 08.06.2015
comment
отличный и простой, но не скользящий - person keybee; 31.07.2015
comment
У меня получилось хорошо, я следил за проектом на Github. Отличное спасибо. - person CodeDaily; 31.03.2016
comment
рад помочь :) наслаждайтесь кодированием - person King of Masses; 31.03.2016
comment
реально гэат хелп ... тысм :) - person Bunny; 06.08.2016
comment
У меня тоже сработало, спасибо !! - person Karan Nagpal; 23.08.2017
comment
это не работает на вкладках, какое-либо решение? @KingofMasses - person Rat-a-tat-a-tat Ratatouille; 27.08.2017
comment
Прекрасно работает .. !! Просто используйте addOnPageChangeListener вместо setOnPageChangeListener, поскольку позже он устарел .. Спасибо .. - person Bhavana; 07.11.2017

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

Итак, в основном, первый / последний элементы в вашем пейджере видов - это подделки, они нужны только для создания правильной анимации, а затем переключения на конец / начало. Пример:

list.add(list.get(0)); //add the first item again as the last, to create the wrap effect
list.add(0, list.get(list.size()-2)); //insert a copy of the last item as the new first item, so we can scroll backwards too

viewPager.setAdapter(list);
viewPager.setCurrentItem(1, false); //default to the second element (because the first is now a fake)

viewPager.setOnPageChangeListener(new OnPageChangeListener() {
private void handleScrollState(final int state) {
        if (state == ViewPager.SCROLL_STATE_IDLE) { //this is triggered when the switch to a new page is complete
            final int lastPosition = viewPager.getAdapter().getCount() - 1;
            if (currentPosition == lastPosition) {
                viewPager.setCurrentItem(1, false); //false so we don't animate
            } else if (currentPosition == 0) {
                viewPager.setCurrentItem(lastPosition -1, false);
            }
        }

}
person Graydyn Young    schedule 29.08.2014
comment
Хорошо работает. Простое и чистое решение. - person Cornel; 09.12.2014
comment
Тем не менее, вы можете столкнуться с некоторой ошибкой, если у вас есть бесконечная прокрутка и настраиваемые переходы PageTransformer. Некоторые переходы нарушают конкретное представление после перехода без анимации (например, viewPager.setCurrentItem (1, false);). Тем не менее, если у вас простой пейджер, это работает. - person Cornel; 30.12.2014

Я пробовал все предложения, решения, библиотеки и т. Д., Но они не являются чисто круговыми и в большинстве случаев не поддерживают только 3 страницы.

Итак, я реализовал круговой пример ViewPager, используя новый ViewPager2, новый ViewPager использует RecyclerView и ViewHolders для обработки повторного использования представлений и работает так, как ожидалось!

TL; DR: GITHUB

В этом примере будет создано приложение с одним действием с ViewPager2 и FragmentPagerAdapter, поддерживающим круговую навигацию между 3 страницами или более.

Я использую альфа-версию библиотеки androidx.viewpager2:viewpager2, но версия 1.0.0-alpha06 - последняя из запланированных перед тем, как Google заморозит API и перейдет на бета-версию.

введите описание изображения здесь

1. Добавьте ViewPager2 библиотеку к зависимостям в build.gradle

dependencies {
    implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha06'
}

2. Добавьте представление ViewPager2 в свой проект:

<androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/vwpHome"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

3. Создайте FragmentStateAdapter адаптер:

getItemCount() нужно вернуть огромное число. (2147483647)

getCenterPage() возвращает центральную страницу на основе огромного числа. Этот метод используется для определения позиции начальной страницы в ViewPager2, в этом случае пользователю нужно провести ˜1073741823 раз, чтобы добраться до конца ViewPager2.

class CircularPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) {

    override fun getItemCount() = Integer.MAX_VALUE

    /**
     * Create the fragment based on the position
     */
    override fun createFragment(position: Int) = HomePagerScreens.values()[position % HomePagerScreens.values().size].fragment.java.newInstance()

    /**
     * Returns the same id for the same Fragment.
     */
    override fun getItemId(position: Int): Long = (position % HomePagerScreens.values().size).toLong()

    fun getCenterPage(position: Int = 0) = Integer.MAX_VALUE / 2 + position
}

HomeScreens - это ENUM с информацией о странице.

enum class HomePagerScreens(@StringRes val title: Int,
                            val fragment: KClass<out Fragment>) {

    HOME_1(R.string.home_1, FragmentHome::class),
    HOME_2(R.string.home_2, FragmentHome::class),
    HOME_3(R.string.home_3, FragmentHome::class)
}

4. Установите адаптер на ViewPager

val circularAdapter = CircularPagerAdapter(supportFragmentManager, lifecycle)
vwpHome.apply {
    adapter = circularAdapter
    setCurrentItem(circularAdapter.getCenterPage(), false)
}
person extmkv    schedule 12.07.2019
comment
На мой взгляд, это должен быть единственный принятый ответ. Большое спасибо, @extmkv! :) - person netzpurist; 24.10.2019
comment
Привет. когда я пытался установить getItemCount() = Integer.MAX_VALUE, я не мог перейти на экран, содержащий окно просмотра. Нет logcat, поэтому я не знаю, что происходит - person Huy Nguyen; 09.10.2020

Только сейчас увидел этот вопрос и нашел решение. Ссылка на решение

Внесите следующие изменения в свою функцию onPageScrollStateChanged. Предположим, у вас есть 4 вкладки.

private int previousState, currentState;

public void onPageScrollStateChanged(int state) {              

            int currentPage = viewPager.getCurrentItem();       //ViewPager Type
            if (currentPage == 3 || currentPage == 0){
                previousState = currentState;
                currentState = state;
                if (previousState == 1 && currentState == 0){

                    viewPager.setCurrentItem(currentPage == 0 ? 3 : 0);

                }

            }

        }

Если переменная состояние находится на существующих вкладках, ее значение равно 1.

Он становится равным 0, когда вы пытаетесь перейти до вашей первой вкладки или после вашей последней вкладки. Итак, сравните предыдущее и текущее состояния, как показано в коде, и все готово.

См. Функцию onPageScrollStateChanged здесь.

Надеюсь, поможет.

person Bhasker    schedule 27.01.2014

Это почти выполняет то, что хочет Антонит, хотя он не позволит вам уйти от A -> C. (Тщательно не тестировался, но, похоже, работает достаточно хорошо)

public class MyAdapter extends PagerAdapter {
    private List<Object> mItems;
    private int mFakeCount = 0;

    public MyAdapter(Context context, List<Object> items) {
        mItems = items;
        mFakeCount = mItems.size()+1;
    }

    @Override
    public int getCount() {
        return mFakeCount;
    }

    @Override
    public Object instantiateItem(View collection, int position) {
        // make the size larger, and change the position
        // to trick viewpager into paging forever
        if (position >= mItems.size()-1) {
            int newPosition = position%mItems.size();

            position = newPosition;
            mFakeCount++;
        }

        // do your layout inflating and what not here.
        // position will now refer to a correct index into your mItems list
        // even if the user has paged so many times that the view has wrapped
    }
}
person NPike    schedule 29.11.2011
comment
Он БУДЕТ отпускать вас от A до ›C, если вы вызовете viewPager.setCurrentItem((int) (adapter.getCount()/2), false); после настройки адаптера. Таким образом, ваш ViewPager будет инициализирован в среднем положении, поэтому вы можете прокручивать в обоих направлениях. - person Bojan Radivojevic Bomber; 22.05.2012
comment
Я не понимаю, как это сработало для вас, ребята. У меня это не сработало ... [AndroidRuntime] FATAL EXCEPTION: main [AndroidRuntime] java.lang.IllegalStateException: PagerAdapter приложения изменил содержимое адаптера без вызова PagerAdapter # notifyDataSetChanged! Ожидаемое количество элементов адаптера: 4, найдено: 5 Идентификатор пейджера: com.rpr.mobile:id/ambient_pager Класс пейджера: класс android.support.v4.view.ViewPager Проблемный адаптер: класс rpr.mobile.droid.adapters.ambient.AmbientPagerAdapter [AndroidRuntime] на android.support.v4.view.ViewPager.populate (ViewPager.java:962) - person Justin; 12.09.2014

Простая идея, как этого добиться. Когда у вас есть массив со страницами, просто добавьте этот массив в другой массив точно посередине.

Например, у вас есть массив из 15 элементов. Так что поместите его в массив из 400 элементов посередине (200 позиций)

Затем просто заполните массив слева и справа, чтобы вы могли перемещаться.

Образец изображения:

введите описание изображения здесь

Как это выглядит?

https://youtu.be/7up36ylTzXk

Пусть говорит код :)

https://gist.github.com/gelldur/051394d10f6f98e2c13d

public class CyclicPagesAdapter extends FragmentStatePagerAdapter {

    public CyclicPagesAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return _fragments.get(position).createInstance();
    }

    @Override
    public int getCount() {
        return _fragments.size();
    }

    public void addFragment(FragmentCreator creator) {
        _fragments.add(creator);
    }

    public interface FragmentCreator {
        Fragment createInstance();
    }

    public void clear() {
        _fragments.clear();
    }

    public void add(FragmentCreator fragmentCreator) {
        _fragments.add(fragmentCreator);
    }

    public void finishAdding() {
        ArrayList<FragmentCreator> arrayList = new ArrayList<>(Arrays.asList(new FragmentCreator[MAX_PAGES]));
        arrayList.addAll(MAX_PAGES / 2, _fragments);

        int mrPointer = _fragments.size() - 1;
        for (int i = MAX_PAGES / 2 - 1; i > -1; --i) {
            arrayList.set(i, _fragments.get(mrPointer));
            --mrPointer;
            if (mrPointer < 0) {
                mrPointer = _fragments.size() - 1;
            }
        }

        mrPointer = 0;
        for (int i = MAX_PAGES / 2 + _fragments.size(); i < arrayList.size(); ++i) {
            arrayList.set(i, _fragments.get(mrPointer));
            ++mrPointer;
            if (mrPointer >= _fragments.size()) {
                mrPointer = 0;
            }
        }
        _fragmentsRaw = _fragments;
        _fragments = arrayList;
    }

    public int getStartIndex() {
        return MAX_PAGES / 2;
    }

    public int getRealPagesCount() {
        return _fragmentsRaw.size();
    }

    public int getRealPagePosition(int index) {
        final FragmentCreator fragmentCreator = _fragments.get(index);
        for (int i = 0; i < _fragmentsRaw.size(); ++i) {
            if (fragmentCreator == _fragmentsRaw.get(i)) {
                return i;
            }
        }
        //Fail...
        return 0;
    }

    private static final int MAX_PAGES = 500;
    public ArrayList<FragmentCreator> _fragments = new ArrayList<>();
    public ArrayList<FragmentCreator> _fragmentsRaw;
}
person Dawid Drozd    schedule 06.10.2015
comment
Очень красивое и чистое решение. Спасибо. У меня только один вопрос. Почему вы решили использовать интерфейс для добавления фрагментов? А не проще ли добавить через фанкцию например addFragment(Fragment fragment)? А затем держать список фрагментов? - person user3448282; 30.11.2016
comment
@ user3448282 Я не хочу хранить тяжелые фрагменты в памяти, поэтому выполняю ленивую инициализацию. - person Dawid Drozd; 30.11.2016
comment
Если не проблема, я хотел бы задать вам еще один вопрос. А как насчет удаления предмета? Это подход: pastebin.com/VKkY4j6Z в порядке? Теперь я получаю java.util.ConcurrentModificationException ошибку, так что, возможно, это не лучшее решение. - person user3448282; 01.12.2016
comment
@ user3448282 не проблема, но вы должны удалить его другим способом. Во время итерации ваши итераторы будут искажены при удалении. Просто stackoverflow.com/q/223918/1052261 - person Dawid Drozd; 01.12.2016
comment
У меня последний вопрос. У вас есть идеи, как правильно удалить адаптер формы элемента? Я хочу удалить элемент и после этого перейти к следующему элементу (например, я удаляю элемент номер 6/30, а затем следующий отображаемый элемент должен быть 6/29). Я знаю, что должен удалить все элементы из fragments списка. После notifyDataSetChanged будет выбран неправильный элемент, потому что изменится вся индексация. Моя идея заключалась в том, чтобы подсчитать, сколько элементов было удалено слева от удаленного элемента, а затем прокрутить до position - numberOfRemovedItemsOnLeft, но в некоторых случаях это работает, а в некоторых - нет. - person user3448282; 06.12.2016
comment
@ user3448282 почему вы просто не помните ID вашего текущего фрагмента? Это циклический адаптер, поэтому независимо от того, индекс это 100 или индекс 10, если это один и тот же фрагмент :) Просто после того, как набор notifyData изменился, перейдите к первому идентификатору, который вы хотите - person Dawid Drozd; 06.12.2016
comment
Не знаю, понял ли я вашу идею :) Удаляю текущий фрагмент, затем удаляю все фрагнеты из списка fragments (чтобы убедиться, что они удаляются во всех циклах). Затем я выполняю notifyDataSetChanged, и вся индексация изменяется, поэтому я не могу просто перейти к элементу position + 1, потому что это, вероятно, другой фрагмент, чем был раньше. У меня есть отсортированные данные по фрагментам, и эти фрагменты имеют свой индикаторный номер, поэтому я не могу перейти к случайному фрагменту :) - person user3448282; 06.12.2016
comment
Вы можете использовать позицию в _fragmentsRaw Или просто добавить HashMap для индекса - имя фрагмента, что вы хотите - person Dawid Drozd; 06.12.2016

У меня есть решение, которое, я считаю, сработает. Не тестировалось, но вот оно:

Обертка вокруг FragmentPagerAdapter как суперкласса:

public abstract class AFlexibleFragmentPagerAdapter extends
        FragmentPagerAdapter
{
    public AFlexibleFragmentPagerAdapter(FragmentManager fm)
    {
        super(fm);
        // TODO Auto-generated constructor stub
    }

    public abstract int getRealCount();

    public abstract int getStartPosition();

    public abstract int transformPosition(int position);
};

Затем стандартная реализация FragmentPagerAdapter создает подкласс этого нового абстрактного класса:

public class SomeFragmentPagerAdapter extends
        AFlexibleFragmentPagerAdapter
{

    private Collection<Fragment> someContainer;

    public SomeFragmentPagerAdapter(FragmentManager fm, Container<Fragment> fs)
    {
        super(fm);
        someContainer = fs;
    }

    @Override
    public int getRealCount() { return someContainer.size(); }

    @Override
    public int getStartPosition() { return 0; }

    @Override
    public int transformPosition(int position) { return position; }
};

И реализация адаптера циклического пейджера.

public class SomeCyclicFragmentPagerAdapter extends
        SomeFragmentPagerAdapter 
{
    private int realCount = 0;
    private int dummyCount = 0;
    private static final int MULTI = 3;

    public SomeFragmentPagerAdapter(FragmentManager fm, ...)
    {
        super(fm);
        realCount = super.getCount();
        dummyCount *= MULTI;
    }

    @Override
    public int getCount() { return dummyCount; }

    @Override
    public int getRealCount() { return realCount; }

    @Override
    public int getStartPosition() { return realCount; }

    @Override
    public int transformPosition(int position)
    {
        if (position < realCount) position += realCount;
        else if (position >= (2 * realCount)) position -= realCount;

        return position;
    }
};

Инициализация ViewPager

    this.mViewPager.setCurrentItem(this.mPagerAdapter.getStartPosition());

И, наконец, реализация обратного вызова:

    @Override
    public void onPageSelected(int position)
    {
        this.mTabHost.setCurrentTab(position % mPagerAdapter.getRealCount());
        this.mViewPager.setCurrentItem(this.mPagerAdapter
                .transformPosition(position));
    }

    @Override
    public void onTabChanged(String tabId)
    {
        int pos = this.mTabHost.getCurrentTab();

        this.mViewPager.setCurrentItem(this.mPagerAdapter
                .transformPosition(pos));
    }
person John    schedule 03.08.2013

  1. Установите для элементов подсчет какое-то большое значение, скажем 500000.
  2. Вернуть из адаптера это значение * количество фактических товаров.
  3. В getItem (int i) измените i на -> i = i% количество фактических элементов.
  4. На onCreate (Bundle savedInstanceState) установите текущий элемент для страницы 500000/2

    public class MainActivity extends FragmentActivity {
    
        private final static int ITEMS_MAX_VALUE = 500000;
        private int actualNumCount = 10;
        private ViewPager mPager = null;
    
        private class OptionPageAdapter extends FragmentStatePagerAdapter {
            public OptionPageAdapter(FragmentManager fm) {
                super(fm);
            }
    
            @Override
            public Fragment getItem(int i) {
                try {
                    i = i % OptionType.values().length;
                    OptionPage optionPage = new OptionPage(i);
                    optionPage.id = i;
                    return optionPage;
                } catch (Exception e) {
                    VayoLog.log(Level.WARNING, "Unable to create OptionFragment for id: " + i, e);
                }
                return null;
            }
    
            @Override
            public int getCount() {
                return ITEMS_MAX_VALUE * actualNumCount;
            }
        }
    
        public static class OptionPage extends Fragment {
            private int id = 0
            @Nullable
            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View mainView = inflater.inflate(R.layout.main_page, container, false);
                return mainView;
            }
    
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            mPager = (ViewPager) findViewById(R.id.main_pager);
            mPager.setOffscreenPageLimit(3);
            mPager.setAdapter(new OptionPageAdapter(this.getSupportFragmentManager()));
            mPager.setCurrentItem(ITEMS_MAX_VALUE/2,false);
    
        }
    
    }
    
person Yan    schedule 15.12.2015

  1. добавьте фальшивую копию последнего элемента в начало вашего списка.
  2. добавить в конце поддельную копию Первого Элемента.
  3. выберите настоящий Первый элемент где-нибудь в коде инициализации:

        mViewPager.setCurrentItem(1);
    
  4. изменить setOnPageChangeListener:

        @Override
        public void onPageSelected(int state) {
            if (state == ITEMS_NUMBER + 1) {
                mViewPager.setCurrentItem(1, false);
            } else if (state == 0) {
                mViewPager.setCurrentItem(ITEMS_NUMBER, false);
            }
        }
    
person Alyk    schedule 13.05.2016

на основе этого подхода

истинный цикл, полоса заголовка пейджера, истинный без обновления прокрутки

private static final int ITEM_NUM = 5 ; // 5 for smooth pager title strip
private void addFragment(String title, String className) {
    if (fragments.size() < ITEM_NUM) {
        Bundle b = new Bundle();
        b.putInt("pos", fragments.size());
        fragments.add(Fragment.instantiate(this, className, b));

    }
    titles.add(title);
    itemList.add(className);
}

просмотр адаптера страницы:

public static class MyFragmentPageAdapter extends FragmentPagerAdapter {

    private HashMap<Long, Fragment> mItems = new HashMap<Long, Fragment>();

    public MyFragmentPageAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public long getItemId(int position) {
        int id = java.lang.System.identityHashCode(fragments.get(position));
        return id;
    }

    @Override
    public Fragment getItem(int position) {
        long id = getItemId(position);
        if (mItems.get(id) != null) {
            return mItems.get(id);
        }
        Fragment f = fragments.get(position);
        return f;
    }

    @Override
    public int getCount() {
        return ITEM_NUM;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        String t = titles.get(getItem(position).getArguments()
                .getInt("pos"));
        return t;
    }

    @Override
    public int getItemPosition(Object object) {
        for (int i = 0; i < ITEM_NUM; i++) {
            Fragment item = (Fragment) fragments.get(i);
            if (item.equals((Fragment) object)) {
                return i;
            }
        }
        return POSITION_NONE;
    }
}

с использованием:

    itemList = new ArrayList<String>();
    fragments = new ArrayList<Fragment>();
    titles = new ArrayList<String>();

    addFragment("Title1", Fragment1.class.getName());
    ...
    addFragment("TitleN", FragmentN.class.getName());

    mAdapter = new MyFragmentPageAdapter(getSupportFragmentManager());
    mViewPager = (ViewPager) findViewById(R.id...);

    mViewPager.setAdapter(mAdapter);
    mViewPager.setCurrentItem(2);
    currPage = 2;
    visibleFragmentPos = 2;

    mViewPager.setOnPageChangeListener(new OnPageChangeListener() {

        @Override
        public void onPageSelected(int curr) {
            dir = curr - currPage;
            currPage = curr;
        }

        private void shiftRight() {
            fragments.remove(0);
            int pos = visibleFragmentPos + 3;
            if (pos > itemList.size() - 1)
                pos -= itemList.size();
            if (++visibleFragmentPos > itemList.size() - 1)
                visibleFragmentPos = 0;
            insertItem(4, pos);

        }

        private void shiftLeft() {
            fragments.remove(4);
            int pos = visibleFragmentPos - 3;
            if (pos < 0)
                pos += itemList.size();
            if (--visibleFragmentPos < 0)
                visibleFragmentPos = itemList.size() - 1;
            insertItem(0, pos);
        }

        private void insertItem(int datasetPos, int listPos) {
            Bundle b = new Bundle();
            b.putInt("pos", listPos);
            fragments.add(datasetPos,
                    Fragment.instantiate(ctx, itemList.get(listPos), b));
        }

        @Override
        public void onPageScrollStateChanged(int state) {

            if (state == ViewPager.SCROLL_STATE_IDLE) {
                if (dir == 1) {
                    shiftRight();
                } else if (dir == -1) {
                    shiftLeft();

                }
                mAdapter.notifyDataSetChanged();
                currPage = 2;
            }
        }
    });
person Alyk    schedule 04.07.2016

Вы можете использовать простой вид из https://github.com/pozitiffcat/cyclicview
Одна из функций является:

Не дублируйте первый и последний виды, вместо этого используйте варианты растрового изображения

Пример:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CyclicView cyclicView = (CyclicView) findViewById(R.id.cyclic_view);
        cyclicView.setAdapter(new CyclicAdapter() {
            @Override
            public int getItemsCount() {
                return 10;
            }

            @Override
            public View createView(int position) {
                TextView textView = new TextView(MainActivity.this);
                textView.setText(String.format("TextView #%d", position + 1));
                return textView;
            }

            @Override
            public void removeView(int position, View view) {
                // Do nothing
            }
        });
    }
}
person Алексей Мальчен&    schedule 13.10.2016

Извините за задержку, но я видел ответы, и я не смог удержать, я тоже должен ответить :).

На вашем адаптере для метода get count выполните следующие действия:

 @Override
    public int getCount() {
        return myList.size() % Integer.MAX_VALUE; 
  }

Это будет работать!

person JasJar    schedule 06.04.2014
comment
Почему это сработает? x % Integer.MAX_VALUE == x для любого x ‹Integer.MAX_VALUE - person Martin Konecny; 16.08.2014