【问题标题】:How to make a ViewPager loop?如何制作 ViewPager 循环?
【发布时间】:2021-11-03 21:49:12
【问题描述】:

我有一个带有一些视图的 ViewPager。我想在最后一个右滑后转到第一个。

我试过了

@Override
public Fragment getItem(int arg0) {
    int i = arg0 % fragmentList.size();
    return fragmentList.get(i);
}

@Override
public int getCount() {
    return fragmentList.size()+1;
}

但是我遇到了一个错误

E/AndroidRuntime(22912): java.lang.IllegalStateException: Fragment already added: RubricFragment{4136cd80 #1 id=0x7f06000c android:switcher:2131099660:0}

【问题讨论】:

标签: android loops android-fragments


【解决方案1】:

一种可能性是像这样设置屏幕:

C' A B C A'

C' 看起来就像 C,但是当您滚动到那里时,它会将您切换到真正的 C。 A' 看起来就像 A,但是当您滚动到那里时,它会将您切换到真正的 A。

我会通过像这样实现onPageScrollStateChanged 来做到这一点:

@Override
public void onPageScrollStateChanged (int state) {
    if (state == ViewPager.SCROLL_STATE_IDLE) {
        int curr = viewPager.getCurrentItem();
        int lastReal = viewPager.getAdapter().getCount() - 2;
        if (curr == 0) {
            viewPager.setCurrentItem(lastReal, false);
        } else if (curr > lastReal) {
            viewPager.setCurrentItem(1, false);
        }
    }
}

请注意,这会调用 setCurrentItem 的替代形式并传递 false 以使跳转立即发生,而不是平滑滚动。

我认为这有两个主要缺点。首先,在到达任一端时,用户必须让滚动稳定下来,然后才能走得更远。其次,这意味着在您的第一页和最后一页中拥有所有视图的第二个副本。根据您的屏幕占用资源的程度,这可能会排除这种技术作为可能的解决方案。

还要注意,由于视图分页器在滚动完成之前不会让点击进入底层控件,因此最好不要为 A' 和 C' 片段设置点击侦听器等。

编辑:现在我自己实现了这个,还有另一个相当大的缺点。当它从 A' 切换到 A 或 C' 切换到 C 时,屏幕会闪烁片刻,至少在我当前的测试设备上是这样。

【讨论】:

  • 嗨,你能找到避免闪烁的方法吗?
  • 我的任务的惊人解决方案!谢谢
  • 这个解决方案有点慢。当快速滚动时,状态不会闲置,并且不能将例如A'替换为A。之后快速滚动(疯狂用户)停止,代码完美运行
  • 这不好,因为循环发生时有延迟,请参阅我的答案以获得更好的解决方案!
  • 如果还有两个片段页面,那么当我用tablayout设置它时呢?我希望我的 tablayout 显示只有 ABC 片段,但这会使其显示 5 (C' A B C A')。
【解决方案2】:

我会在 ViewPager 的末尾创建一个虚拟页面。 然后,当用户滚动到虚拟页面时,我使用此代码转到第一页。我知道它远非完美:D

@Override
public void onPageScrolled(int position, float arg1, int arg2) {
    if (position >= NUM_PAGE-1) {
        mViewPager.setCurrentItem(0, true);
    }
}

【讨论】:

  • 如果你这样做,是的,你可以实现无限滚动,但在最后一页滚动时,你会看到像回到scren1一样的动画..
【解决方案3】:

我的方案是基于benkc,但是禁用了首页和末页滚动动画,当页面“滚动”到真实页面时,再次启用滚动动画,这个方案可以解决第一个缺点。

但我的ViewPager.setCurrentItem(position, false) 结果仍然有滚动动画,所以我实现了动画太快而无法看到。

这样的快速滚动动画,不要介意评论,只是我的代码没有使用这些方法:

public class FixedSpeedScroller extends Scroller {
    private int mDuration = 0;

    public FixedSpeedScroller(Context context) {
        super(context);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        super.startScroll(startX, startY, dx, dy, mDuration);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        super.startScroll(startX, startY, dx, dy, mDuration);
    }
}

并使用此方法查看pager的活动

private Scroller scroller;
private void setViewPagerScroll(boolean instant) {
    try {
        Field mScroller = null;
        mScroller = ViewPager.class.getDeclaredField("mScroller");
        mScroller.setAccessible(true);
        if (scroller == null) {
            scroller = (Scroller) mScroller.get(mViewPager);
        }
        FixedSpeedScroller fss = new FixedSpeedScroller(mViewPager.getContext());
        mScroller.set(mViewPager, instant ? fss : scroller);

    } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
    }
}

并像这样修改onPageScrollStateChanged,只有第一页或最后一页(我有5页)会将动画更改为快速滚动,否则正常滚动:

public void onPageScrollStateChanged(int state) {
    if (state == ViewPager.SCROLL_STATE_IDLE) {
        if (position == 0) {
            setViewPagerScroll(true);
            mViewPager.setCurrentItem(3);

        } else if (position == 4) {
            setViewPagerScroll(true);
            mViewPager.setCurrentItem(1);

        } else {
            setViewPagerScroll(false);
        }
    }
}

FixedSpeedScroller 参考在这里:http://blog.csdn.net/ekeuy/article/details/12841409

【讨论】:

    【解决方案4】:

    这应该可以在没有虚拟页面的情况下完成工作:

    private boolean isFirstOrLastPage;
    private int currentPageIndex = 0;
    
    
    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
        if(currentPageIndex!=arg0){
            isFirstOrLastPage = false;
            return;
        }
        if((arg0==0 || arg0==PAGES.size()-1) && arg1 == 0 && arg2 == 0){
            if(isFirstOrLastPage){
                     //DO SOMETHING
            }else{
                isFirstOrLastPage = true;
            }
        }
    }
    
    @Override
    public void onPageSelected(int arg0) {
        currentPageIndex = arg0;
    }
    

    【讨论】:

      【解决方案5】:

      这行得通,接受的答案不好,因为循环发生时存在滞后:

       @Override
      public int getCount() {
          return Integer.MAX_VALUE;
      }
      
      @Override
      public CharSequence getPageTitle(int position) {
          String title = mTitleList.get(position % mActualTitleListSize);
          return title;
      }
      
      @Override
      public Object instantiateItem(ViewGroup container, int position) {
          int virtualPosition = position % mActualTitleListSize;
          return super.instantiateItem(container, virtualPosition);
      }
      
      @Override
      public void destroyItem(ViewGroup container, int position, Object object) {
          int virtualPosition = position % mActualTitleListSize;
          super.destroyItem(container, virtualPosition, object);
      }
      

      答案取自这里:ViewPager as a circular queue / wrapping

      【讨论】:

        【解决方案6】:

        Kotlin 版本:

        初始化变量

        private var mCurrentPosition = 0
        private var mScrollState = 0
        private lateinit var mImageViewPager: ViewPager
        

        创建:

        mImageViewPager = findViewById<View>(R.id.pager) as ViewPager
        
        mImageViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
        
                    override fun onPageScrollStateChanged(state: Int) {
                        handleScrollState(state)
                        mScrollState = state
                    }
        
                    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        
                    }
                    override fun onPageSelected(position: Int) {
                        mCurrentPosition = position
                    }
        
                })
        

        onCreate 之外的函数:

        private fun handleScrollState(state: Int) {
        
                if (state == ViewPager.SCROLL_STATE_IDLE) {
        
                    if (!isScrollStateSettling()){
        
                        val lastPosition: Int = mImageViewPager.getAdapter()?.getCount()!! - 1
                        if (mCurrentPosition == 0) {
                            mImageViewPager.setCurrentItem(lastPosition , false)
                        } else if (mCurrentPosition == lastPosition) {
                            mImageViewPager.setCurrentItem(0 , false)
                        }
                    }
                }
            }
        
        private fun isScrollStateSettling(): Boolean {
                return mScrollState == ViewPager.SCROLL_STATE_SETTLING
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-01-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-29
          • 2020-10-16
          相关资源
          最近更新 更多