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

голоса
60

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

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

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

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

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

Задан 25/09/2011 в 15:42
источник пользователем
На других языках...                            


12 ответов

голоса
47

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

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

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

https://github.com/antonyt/InfiniteViewPager

Ответил 11/03/2012 в 17:51
источник пользователем

голоса
13

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

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));
Ответил 08/06/2015 в 09:26
источник пользователем

голоса
6

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

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

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

}
Ответил 29/08/2014 в 20:09
источник пользователем

голоса
6

Это почти выполняет то, что antonyt хочет, оно не отпустит вас от А -> 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
    }
}
Ответил 29/11/2011 в 01:55
источник пользователем

голоса
5

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

Внести следующие изменения в 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 функции здесь .

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

Ответил 27/01/2014 в 11:14
источник пользователем

голоса
1

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

Например у вас есть массив с 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;
}
Ответил 06/10/2015 в 22:11
источник пользователем

голоса
0

Вы можете использовать простой вид из 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
            }
        });
    }
}
Ответил 13/10/2016 в 14:18
источник пользователем

голоса
0

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

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

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;
            }
        }
    });
Ответил 04/07/2016 в 08:54
источник пользователем

голоса
0
  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);
            }
        }
    
Ответил 13/05/2016 в 14:58
источник пользователем

голоса
0
  1. Набор для подсчитывать элементы некоторое большое значение, скажем, 500000.
  2. Возвращение из адаптера этого значения * число фактических деталей.
  3. На GetItem (INT я) настроить 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);
    
        }
    
    }
    
Ответил 15/12/2015 в 15:15
источник пользователем

голоса
0

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

На адаптере, на счет метода GET, действуйте следующим образом:

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

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

Ответил 06/04/2014 в 17:28
источник пользователем

голоса
0

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

Обертка вокруг 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));
    }
Ответил 03/08/2013 в 19:58
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more