我想实现一个使用Fragments的ViewPager,并且可以在一个曲面运动中滑动,例如页面(A - B - C - A)。 我已经阅读了几篇关于如何完成此事的帖子,例如:返回有多少元素的虚假计数,并在中间开始设置位置。 how to create circular viewpager?
这些似乎都是基于PagerAdapter。当我在扩展FragmentPagerAdapter时尝试做类似的事情时,只要我返回一个fakeCount页面,当我浏览片段时我得到一个例外,我只有2个片段。 异常:java.lang.IllegalStateException:无法更改片段的标记。
我认为这是因为FragmentManager认为我处于位置2但位置2指向位置0处的片段。是否有人知道如何避免这种情况?我在想我应该尝试扩展Fragmentmanager。任何示例或对此的帮助将不胜感激。
答案 0 :(得分:3)
我知道这有点晚了,但这对我有用:
我需要在3个片段之间进行循环滑动,因此我将这3个和2个虚拟片段用于帮助我实现页面循环:
public static class FirstViewFragment extends Fragment {
// Empty Constructor
public FirstViewFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_1, container, false);
}
}
public static class SecondViewFragment extends Fragment {
// Empty Constructor
public SecondViewFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_2, container, false);
}
}
public static class ThirdViewFragment extends Fragment {
// Empty Constructor
public ThirdViewFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_3, container, false);
}
}
另外两个虚拟碎片使我能够从第一个向右滑动,从最后一个向右滑动。第一个虚拟膨胀与最后一个虚拟相同的布局和最后一个虚拟与第一个实际相同的布局:
public static class StartVirtualFragment extends Fragment {
public StartVirtualFragment() {}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_3, container, false);
}
}
public static class EndVirtualFragment extends Fragment {
public EndVirtualFragment() {}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_1, container, false);
}
}
我的适配器:
private class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new StartVirtualFragment();
case 1:
if (firstViewFragment == null) {
firstViewFragment = new FirstViewFragment();
}
return firstViewFragment;
case 2:
if (secondViewFragment == null) {
secondViewFragment = new SecondViewFragment();
}
return secondViewFragment;
case 3:
if (thirdViewFragment == null) {
thirdViewFragment = new ThirdViewFragment();
}
return thirdViewFragment;
case 4:
return new EndVirtualFragment();
}
return null;
}
@Override
public int getCount() {
return 5;
}
}
我的页面监听器我使用onPageScrollStateChanged来设置正确的页面并实现循环:
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING) {
int pageCount = viewPager.getChildCount();
int currentItem = viewPager.getCurrentItem();
if (currentItem == 0) {
viewPager.setCurrentItem(pageCount - 2, false);
} else if (currentItem == pageCount - 1) {
viewPager.setCurrentItem(1, false);
}
}
}
});
最后:
viewPager.setCurrentItem(1);
希望我帮助
答案 1 :(得分:1)
我在GitHub中有一个项目,我创建了一些小部件。它在这里:
https://github.com/CyberEagle/AndroidWidgets
在以下包中,有一些与CircularViewPager一起使用的适配器: https://github.com/CyberEagle/AndroidWidgets/tree/master/src/main/java/br/com/cybereagle/androidwidgets/adapter
首先,您将在布局中使用CircularViewPager而不是ViewPager。 CircularViewPager位于:https://github.com/CyberEagle/AndroidWidgets/blob/master/src/main/java/br/com/cybereagle/androidwidgets/view/CircularViewPager.java
此ViewPager需要一个WrapperCircularPagerAdapter,而不是PagerAdapter。这个包装器用于欺骗ViewPager,使其认为ViewPager中有很多项目,但实际上它会重复您的项目以产生循环效果。因此,您将实现CircularFragmentPagerAdapter,CircularFragmentStatePagerAdapter或CircularPagerAdapter,而不是实现PagerAdapter,FragmentPagerAdapter或FragmentStatePagerAdapter。然后,您将使用WrapperCircularPagerAdapter包装适配器,并在CircularViewPager中设置包装器,而不是您的适配器。此外,当通知数据集发生变化时,您将在包装器中调用notifyDatasetChanged()。
实现其中一个循环适配器时,您会注意到实现instantiateVirtualItem而不是实现instantiateItem。对于片段的寻呼机适配器,您将实现getVirtualItem而不是getItem。那是因为我创造了虚拟物品的概念。
为了清楚起见,想象一下包含4个项目的视图寻呼机,每个项目代表一个音乐。当你一直走到左边时,你会看到第一个左边的第4个项目。实际上,它是一个全新的项目,但它与代表第四音乐的虚拟项目相关联。
另一个例子:想象现在只有一首音乐。您将在左侧和右侧看到相同的音乐。一次只有3个项目,但只有一个虚拟项目。
因此,正如所解释的那样,Wrapper正在欺骗ViewPager,让它认为有很多项目。为了使用户更难以到达ViewPager的一端(无论如何都需要很长时间),每次数据集发生变化时,ViewPager都会转到同一个虚拟项目,但会转到其中一个中间附近的实物。
更重要的是,CircularViewPager具有setCurrentVirtualItem方法。此方法计算哪个实际项目是最近的所需虚拟项目,然后使用setCurrentItem进行设置。您还可以选择使用getCurrentVirtualItem,它将返回当前虚拟项的索引。请注意,如果使用getCurrentItem,您将获得一个大索引。
嗯,就是这样。我很抱歉缺少项目文档。我计划很快就把它记录下来。我还打算不再需要包装器。随意复制代码(尊重Apache 2.0许可证),分叉甚至贡献它。
答案 2 :(得分:-2)
**If you want to make 3 views visible at same time and make it circular**
public abstract class CircularPagerAdapter extends PagerAdapter{
private int count;
int[] pagePositionArray;
public static final int EXTRA_ITEM_EACH_SIDE = 2;
private ViewPager.OnPageChangeListener pageChangeListener;
private ViewPager viewPager;
public CircularPagerAdapter(final ViewPager pager, int originalCount ) {
super();
this.viewPager = pager;
count = originalCount + 2*EXTRA_ITEM_EACH_SIDE;
pager.setOffscreenPageLimit(count-2);
pagePositionArray = new int[count];
for (int i = 0; i < originalCount; i++) {
pagePositionArray[i + EXTRA_ITEM_EACH_SIDE] = i;
}
pagePositionArray[0] = originalCount - 2;
pagePositionArray[1] = originalCount -1;
pagePositionArray[count - 2] = 0;
pagePositionArray[count - 1] = 1;
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageSelected(final int position) {
if(pageChangeListener != null)
{
pageChangeListener.onPageSelected(pagePositionArray[position]);
}
pager.post(new Runnable() {
@Override
public void run() {
if (position == 1){
pager.setCurrentItem(count-3,false);
} else if (position == count-2){
pager.setCurrentItem(2,false);
}
}
});
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(pageChangeListener != null)
{
pageChangeListener.onPageScrolled(pagePositionArray[position],positionOffset,positionOffsetPixels);
}
}
public void onPageScrollStateChanged(int state) {
if(pageChangeListener != null)
{
pageChangeListener.onPageScrollStateChanged(state);
}
}
});
}
@Override
public int getCount() {
return count;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return false;
}
public abstract Object customInstantiateItem(ViewGroup container, int position);
public void setPageChangeListener(ViewPager.OnPageChangeListener pageChangeListener)
{
this.pageChangeListener = pageChangeListener;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
int pageId = pagePositionArray[position];
return customInstantiateItem(container,pageId);
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
public void setFirstItem()
{
viewPager.setCurrentItem(EXTRA_ITEM_EACH_SIDE - 1);
}
}