ViewPager作为循环队列/包装
我正在使用ViewPager
和FragmentStatePagerAdapter
来允许在一些片段之间导航。
假设我有三个片段: A
, B
和C
ViewPager最初显示片段A,并允许您通过从右向左滑动导航到片段B,然后再次滑动到片段C. 这允许以下导航path: A <--> B <--> C
我希望能够在片段A上从左到右滑动,并让ViewPager显示片段C,也就是让它看起来像循环队列,并允许... --> C <--> A <--> B <--> C <--> A <-- ...
我不希望碎片在其他位置重复(即结束超过三个实例)。
这个包装function可能与ViewPager?
我已经实现了一个ViewPager / PagerAdapter,可以允许伪无限分页行为。 它通过指定一个非常大的数字作为实际计数,但将其映射到数据集/页面集的实际范围。 它也使大量数据开始偏移,以便您可以立即从“第一个”页面向左滚动。
一旦达到第1,000,000页(当滚动时会出现graphics故障),它效果不佳,但这通常不是真实世界的用例。 我可以通过将计数重置为一个较低的数字来解决这个问题,但是我现在就不会这么做了。
InfinitePagerAdapter
封装了一个现有的ViewPager,所以用法非常透明。 InfiniteViewPager
会做一些工作,以确保您可以多次滚动到左侧和右侧。
这是一个没有伪造页面的解决scheme,像一个魅力:
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的“结尾”出现这个蓝色的光芒,你应该把这一行应用到放置ViewPager的xml中:
android:overScrollMode="never"
我们已经改进了上面的解决scheme,并在github上创build了一个小库。 随意检查一下:)
编辑::根据@Bhavana评论,只要使用addOnPageChangeListener而不是setOnPageChangeListener,因为以后不推荐使用。
viewPager.addOnPageChangeListener(new CircularViewPagerHandler(viewPager));
这几乎完成了antonyt想要的,但是不会让你从A – > C去。 (没有大力testing,但似乎足够好)
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 } }
刚才看到这个问题,find了解决办法。 解决scheme链接
对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); } } }
如果状态variables在现有选项卡中,则其值为1。
一旦您尝试在第一个标签之前或最后一个标签之后导航,它就会变成0。 因此,比较以前和目前的状态,如代码所示,你完成了。
请参阅onPageScrollStateChanged函数。
希望能帮助到你。
另一种方法是将视图分页器中第一个元素的假副本添加到适配器的末尾,并将最后一个元素的假副本添加到开头。 然后,当您查看视图寻呼机中的第一个/最后一个元素时,无需animation即可更改页面。
所以基本上,视图分页器中的第一个/最后一个元素是假的,只是在那里产生正确的animation,然后切换到结束/开始。 例:
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); } } }
简单的想法如何实现这一点。 当你有你的数组与网页时,只需将这个数组完全添加到另一个数组中。
例如,你有15个元素的数组。 所以把它放到中间的400元素arrays(200位置)
接下来只需在左右方向上填充数组即可左右移动。
示例图像:
它看起来如何?
让代码说话:)
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; }
我有一个解决scheme,我相信会工作。 它没有testing,但在这里是:
作为超类的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());
最后,callback实现:
@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)); }
对不起,延迟,但我已经看到了答案,二无法持有,也得到回答了:)。
在适配器上,在get count方法上,按以下步骤操作:
@Override public int getCount() { return myList.size() % Integer.MAX_VALUE; }
那可行!
- 设置的项目计数一些大的价值,比如500000。
- 从适配器返回此值*实际项目的数量。
- 在getItem(int i)调整i – > i = i%实际项目数。
-
在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); } }
- 在列表开始处添加最后一个元素的伪造副本。
- 最后添加第一个元素的假复制品。
-
在init代码中select真正的第一个元素:
mViewPager.setCurrentItem(1);
-
修改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); } }
基于这种方法
真正的循环,寻呼机标题栏,真正的没有刷新滚动
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; } } });
您可以使用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 } }); } }