7

I would like to implement a ViewPager which uses Fragments and can be swiped in a curcular motion e.g. Page (A<-->B<-->C<-->A). I have read a couple of posts on how this is done, e.g. returning a fake count of how many elements there are and setting the position at the start in the middle. how to create circular viewpager?

These all seem to be based of a PagerAdapter. When I try to do a similar thing while extending FragmentPagerAdapter, as soon as I return a fakeCount of pages I get an exception when I Swipe through my Fragments, I only have 2 Fragments. Exception: java.lang.IllegalStateException: Can't change tag of fragment.

I think this is caused as the FragmentManager thinks I am in position 2 but position 2 points to the fragment at position 0. Does anyone know how I can avoid this? I am thinking I should experiment with extending Fragmentmanager. Any examples or help with this would be greatly appreciated.

4

3 回答 3

3

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

我需要在 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);

希望我有所帮助

于 2014-05-12T10:55:56.513 回答
1

I have a project in the GitHub with some widgets I've created. Here it its:

https://github.com/CyberEagle/AndroidWidgets

In the following package, there are the adapters to be used with the CircularViewPager: https://github.com/CyberEagle/AndroidWidgets/tree/master/src/main/java/br/com/cybereagle/androidwidgets/adapter

First, you will use CircularViewPager instead of ViewPager in your layout. The CircularViewPager is here: https://github.com/CyberEagle/AndroidWidgets/blob/master/src/main/java/br/com/cybereagle/androidwidgets/view/CircularViewPager.java

This ViewPager expects a WrapperCircularPagerAdapter, instead of a PagerAdapter. This wrapper is used to trick the ViewPager, making it to think there are a lot of items in the ViewPager, but it actually repeat your items to make the circular effect. So, instead of implementing either PagerAdapter, FragmentPagerAdapter or FragmentStatePagerAdapter, you will implement either CircularFragmentPagerAdapter, CircularFragmentStatePagerAdapter or CircularPagerAdapter. Then, you will wrap your adapter with the WrapperCircularPagerAdapter and set the wrapper in the CircularViewPager, instead of your adapter. Also, when it's time to notify dataset changed, you will call the notifyDatasetChanged() in the wrapper.

When implementing one of the circular adapter, you will notice that instead of implementing instantiateItem, you will have to implement instantiateVirtualItem. For the fragment's pager adapter, you will implement getVirtualItem instead of getItem. That is because I've created the concept of virtual items.

To make it clear, imagine a view pager with 4 items, giving that each item represents a music. When you go all the way to left, you will see the 4th item in the left of the first. Actually, it's a whole new item, but it's linked to the virtual item that represents the 4th music.

Another example: imagine there's only one music now. You will see the same music on the left and on the right. There're 3 items at a time, but only one virtual item.

So, as explained, the Wrapper is tricking the ViewPager, making it think that there are a lot of items. To make it more difficult for the user to reach one of the ends of the ViewPager (it'd take a long time anyway), everytime a change happens to the dataset, the ViewPager goes to the same virtual item, but to one of the real items near the middle.

One more important thing is that the CircularViewPager has the method setCurrentVirtualItem. This method calculates which real item is the nearest desired virtual item and then it uses the setCurrentItem to set it. You have also the option to use the getCurrentVirtualItem, that will return the index of the current virtual item. Notice that if you use getCurrentItem, you'll get a large index.

Well, this is it. I'm sorry for the lack of documentation of the project. I'm planning document it soon. I'm also planning to remove the need for the wrapper. Feel free to copy the code (respecting the Apache 2.0 license), to fork or even contribute to it.

于 2013-10-17T23:34:22.067 回答
-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);
        }
    }
于 2016-09-07T05:52:53.403 回答