ViewPager作为循环队列/包装

ant*_*nyt 64 android android-fragments android-viewpager

我使用的是ViewPagerFragmentStatePagerAdapter让一些片段之间的导航.

比方说,我有三个片段:A,BC.ViewPager最初显示片段A,并允许您通过从右向左滑动,然后再次滑动到片段C来导航到片段B. 这允许以下导航路径:A <--> B <--> C.

我想要的是能够在片段A上从左向右滑动并让ViewPager显示片段C,即它表现为循环队列并允许 ... --> C <--> A <--> B <--> C <--> A <-- ...

我不希望片段在其他位置重复(即最终有三个以上的实例).

使用ViewPager可以实现这种包装功能吗?

ant*_*nyt 49

我已经实现了一个ViewPager/PagerAdapter,它可以允许伪无限的分页行为.它通过指定一个非常大的数字作为实际计数,但将它们映射到数据集/页面集的实际范围.它还会使开头偏移很多,以便您可以立即从"第一页"向左滚动.

一旦你到达第1,000,000页(在滚动时你会看到图形故障),它不能很好地工作,但这通常不是真实世界的用例.我可以通过偶尔将计数重置为较低的数字来解决这个问题,但我现在将其保留原样.

InfinitePagerAdapter包装现有ViewPager,所以使用是相当透明的.这InfiniteViewPager确实做了一些工作,以确保您可以多次向左和向右滚动.

https://github.com/antonyt/InfiniteViewPager

  • 页数小于4时有问题. (15认同)
  • 你好,antonyt.如何修复少于4个碎片?我在这里看到你的答案https://github.com/antonyt/InfiniteViewPager/issues/2你能帮帮我吗?我需要交换3个片段,但我无法解决这个问题.如何阻止destroyItem()/调用instantiateItem()? (4认同)

Kin*_*ses 16

这是一个没有虚假页面的解决方案,就像一个魅力:

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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

你只需要将它设置为你的ViewPager作为onPageChangeListener,就是这样:(**现在不推荐**检查编辑说明)

viewPager.setOnPageChangeListener(new CircularViewPagerHandler(viewPager));
Run Code Online (Sandbox Code Playgroud)

为了避免在ViewPager的"结尾"出现这种蓝色光芒,您应该将此行应用于放置ViewPager的xml:

android:overScrollMode="never"
Run Code Online (Sandbox Code Playgroud)

我们改进了上面的解决方案并在github上创建了一个小库.随意查看:)

编辑::根据@Bhavana注释,只需使用addOnPageChangeListener而不是setOnPageChangeListener,因为不推荐使用以后版本.

 viewPager.addOnPageChangeListener(new CircularViewPagerHandler(viewPager));
Run Code Online (Sandbox Code Playgroud)

  • 伟大而简单,但它并不滑动 (2认同)
  • 为我做得很好,我跟着Github的项目.很好,谢谢. (2认同)
  • 完美的工作.. !! 只是使用addOnPageChangeListener而不是setOnPageChangeListener,因为以后不推荐使用..谢谢.. (2认同)

Bha*_*ker 8

刚才看到这个问题并找到了解决方案.解决方案链接

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

                }

            }

        }
Run Code Online (Sandbox Code Playgroud)

如果状态变量位于现有选项卡中,则其值为1.

一旦您尝试第一个选项卡之前或最后一个选项卡之后导航,它就会变为0 .因此,比较前一个和当前状态,如代码中所示,您就完成了.

在此处查看onPageScrollStateChanged函数.

希望能帮助到你.


Gra*_*ung 8

另一种方法是将视图分页器中第一个元素的伪副本添加到适配器的末尾,并将最后一个元素的伪副本添加到开头.然后,当您查看视图寻呼机中的第一个/最后一个元素时,无需动画即可更改页面.

所以基本上,视图寻呼机中的第一个/最后一个元素是假的,只是产生正确的动画,然后切换到结束/开始.例:

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

}
Run Code Online (Sandbox Code Playgroud)


NPi*_*ike 6

这几乎完成了antonyt想要的东西,它不会让你从A - > 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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果在设置适配器后调用`viewPager.setCurrentItem((int)(adapter.getCount()/ 2),false);`,它将允许你从A - > C开始.这样,您的ViewPager将初始化为中间位置,因此您可以向两个方向滚动. (2认同)

ext*_*mkv 6

我一直在尝试所有建议,解决方案,库等,但是它们并不是纯循环的,并且大多数时候不仅仅支持3页。

因此,我ViewPager使用new 实现了一个循环示例ViewPager2,而new ViewPager使用a RecyclerViewViewHolders处理视图回收并按预期工作!

TLDR: GITHUB

在此示例中,将构建一个活动应用程序,ViewPager2FragmentPagerAdapter在3页或更多页面之间提供循环导航。

我使用的是库的Alpha版本androidx.viewpager2:viewpager2,但该版本1.0.0-alpha06是Google冻结API并移至Beta之前计划的最后一个版本。

在此处输入图片说明

1.将ViewPager2库添加到build.gradle中的依赖项

dependencies {
    implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha06'
}
Run Code Online (Sandbox Code Playgroud)

2.将ViewPager2视图添加到您的项目中:

dependencies {
    implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha06'
}
Run Code Online (Sandbox Code Playgroud)

3.创建FragmentStateAdapter适配器:

getItemCount()需要返回一个huuuuge数字。(2147483647)

getCenterPage()根据huuuuge编号返回中心页面。该方法用于获取要设置的初始页面的位置ViewPager2,在这种情况下,用户需要滑动〜1073741823时间才能到达的末尾ViewPager2

class CircularPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) {

    override fun getItemCount() = Integer.MAX_VALUE

    /**
     * Create the fragment based on the position
     */
    override fun createFragment(position: Int) = HomePagerScreens.values()[position % HomePagerScreens.values().size].fragment.java.newInstance()

    /**
     * Returns the same id for the same Fragment.
     */
    override fun getItemId(position: Int): Long = (position % HomePagerScreens.values().size).toLong()

    fun getCenterPage(position: Int = 0) = Integer.MAX_VALUE / 2 + position
}
Run Code Online (Sandbox Code Playgroud)

HomeScreens是带有页面信息的枚举。

enum class HomePagerScreens(@StringRes val title: Int,
                            val fragment: KClass<out Fragment>) {

    HOME_1(R.string.home_1, FragmentHome::class),
    HOME_2(R.string.home_2, FragmentHome::class),
    HOME_3(R.string.home_3, FragmentHome::class)
}
Run Code Online (Sandbox Code Playgroud)

4.将适配器设置为ViewPager

val circularAdapter = CircularPagerAdapter(supportFragmentManager, lifecycle)
vwpHome.apply {
    adapter = circularAdapter
    setCurrentItem(circularAdapter.getCenterPage(), false)
}
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,这应该是唯一可以接受的答案。非常感谢,@extmkv!:) (2认同)