【问题标题】:Circular ViewPager which uses FragmentPagerAdapter使用 FragmentPagerAdapter 的圆形 ViewPager
【发布时间】:2012-08-27 11:46:08
【问题描述】:

我想实现一个 ViewPager,它使用 Fragments 并且可以以曲线运动方式滑动,例如页(ABCA)。 我已经阅读了几篇关于如何做到这一点的帖子,例如返回有多少元素的假计数,并将位置设置在中间的开头。 how to create circular viewpager?

这些似乎都基于 PagerAdapter。当我在扩展 FragmentPagerAdapter 时尝试做类似的事情时,只要我返回一个 fakeCount 的页面,当我扫过我的片段时就会得到一个异常,我只有 2 个片段。 异常:java.lang.IllegalStateException:无法更改片段的标记。

我认为这是因为 FragmentManager 认为我在位置 2 但位置 2 指向位置 0 的片段。有谁知道我该如何避免这种情况?我想我应该尝试扩展 Fragmentmanager。任何示例或帮助将不胜感激。

【问题讨论】:

  • 我已经为此实施了一个解决方案。我快完成测试了,我会在完成后在这里回答。
  • @FernandoCamargo 你解决了吗??
  • 是的。对不起,我忘了在这里回答。我现在就发布答案。

标签: android android-fragments android-viewpager


【解决方案1】:

我知道这有点晚了,但这对我来说是这样的:

我需要在 3 个片段之间进行循环滑动,因此我将这 3 个片段和两个片段设为虚拟以帮助我实现页面循环:

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

希望我能帮上忙

【讨论】:

    【解决方案2】:

    我在 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 许可)、分叉甚至贡献代码。

    【讨论】:

    • 我已经实现了你提到的 View Pager。而且我正在使用你上面提到的类 CircularViewPager、WrapperCircularPagerAdapter 和 CircularFragmentPagerAdapter。但仍然得到异常:异常:java.lang.IllegalStateException:无法更改片段的标签。有什么建议吗?
    • 如果我添加 4 个片段,那么我的代码可以正常工作。但我的要求是只使用 2 个导致上述异常的片段
    • @MiaNKhaLiD,这很奇怪。即使只有一个片段,它也对我有用。你能告诉我你是如何使用它的吗?
    • 实际上我也在尝试重用片段,所以,我在你的类 CircularPagerAdapter 中添加了一些代码。代码链接:Sample Dropbox Link 目前,如果您运行代码,它将有 4 个片段/页面,并且运行良好。但如果你将它们更改为 2,应用程序会在启动时崩溃。
    • @MiaNKhaLiD,我认为您不能重用片段的实例。至少,ViewPager 将尝试拥有 3 个片段:可见片段、左侧片段和右侧片段。如果您有 2 个实例,则不能有 3 个片段。所以,让相同类型的片段的多个实例被创建。无法重复使用该片段。
    【解决方案3】:
    **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);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2012-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-18
      • 2015-05-13
      相关资源
      最近更新 更多