【问题标题】:How to add endless/infinite scroll to ViewPager2?如何向 ViewPager2 添加无限/无限滚动?
【发布时间】:2019-11-06 17:15:58
【问题描述】:

我有一个附加到新 ViewPager2 的 FragmentStateAdapter。在之前的 ViewPager 实现中,有多种方法可以实现无限/无限滚动。在 ViewPager2 中怎么做?

提前致谢。

【问题讨论】:

    标签: android android-viewpager2


    【解决方案1】:

    这是我想出的:

    class EndlessScrollAdapter internal constructor(
        fm: FragmentManager,
        lifeCycle: Lifecycle
    ) : FragmentStateAdapter(fm, lifeCycle) {
    
        private val items = mutableListOf<YourModel>()
        val firstElementPosition = Int.MAX_VALUE / 2
    
        fun updateList(list: List<YourModel>) {
            items.apply {
                clear()
                addAll(list)
            }
            notifyDataSetChanged()
        }
    
        override fun getItemCount(): Int = if (items.isNotEmpty()) Int.MAX_VALUE else 0
    
        override fun createFragment(position: Int): Fragment = YourPagerFragment(
            items[position.rem(items.size)])
    }
    

    在Activity或Fragment中我们需要调用:

    viewPager2.adapter = endlessScrollAdapter
    endlessScrollAdapter.apply {
      updateList(yourModelList)
      viewPager2.setCurrentItem(this.firstElementPosition, false)
    }
    

    从字面上看,它不是无穷无尽的,但从用户的角度来看,它是无穷无尽的,因为他永远不会到达边缘。 ViewPager2 的长度是Int.MAX_VALUE,起始位置是Int.MAX_VALUE/2,所以用户可以前后滚动。

    【讨论】:

    • 什么是 ViewPagerFragment?我缺少代码吗?
    • @user1801179 这只是你自己的片段。它不是内置的 Android 类。在上述情况下,我对 ViewPager2 中的所有页面使用相同的片段 - ViewPagerFragment。
    【解决方案2】:

    要使用 viewpager 2 实现无限滑动,我们可以执行以下操作:-

    1. 在开头添加最后一个元素的副本,在末尾添加第一个元素的副本。
    originalList.add(0, lastElement) // last element at the first position
    originalList.add(firstElement)   // first element at the last
    
    1. 设置适配器后,确保将当前项设置为 1。
    viewpager.setCurrentItem(1, false)
    
    1. 设置适配器后,从 viewpager 组件中提取 recyclerView 并向其添加以下滚动侦听器。
    val recyclerView = viewpager.getChildAt(0) as RecyclerView
    val layoutManager = recyclerView.layoutManager as LinearLayoutManager
    val itemCount = viewpager.adapter?.itemCount ?: 0
    // attach scroll listener
    recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
         override fun onScrolled(
            recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val firstItemVisible 
                = layoutManager.findFirstVisibleItemPosition()
            val lastItemVisible 
                = layoutManager.findLastVisibleItemPosition()
            if (firstItemVisible == (itemCount - 1) && dx > 0) {
                recyclerView.scrollToPosition(1)
            } else if (lastItemVisible == 0 && dx < 0) {
                recyclerView.scrollToPosition(itemCount - 2)
            }
        }
    })
    

    在这三个步骤之后,我们可以在 viewpager2 中无限滚动/滑动。

    致谢:Viewpager 2 infinite swipe

    【讨论】:

      【解决方案3】:

      我对 android 很陌生,我花了一整天的时间阅读我能找到的关于这个问题的所有帖子。我还有一个 FragmentStateAdapter,其中有 3 个 Fragment 附加到 viewPager2。

      我的解决方案是覆盖 OnPageChangeCallback 事件。在这里,我将 state 和 currentPosition 保存在 2 个局部变量中;然后,在onPageScrolled,我添加了一个控件来理解:

      1. 如果滑动事件开始
      2. 如果新位置等于当前位置。观察日志,我注意到只有当我尝试从第一页向左滚动或从最后一页向右滚动时它们是相同的。
      3. 如果页面位置是第一个还是最后一个

      如果所有这些条件都成立并且我在第一页,那么我会将下一页设置为最后一页,反之亦然。

      这是我的代码。我使用了 0 和 2,因为我有 3 个片段。

      viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback()
      {
          private int myState;
          private int currentPosition;
      
          @Override
          public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
          {
              if (myState == ViewPager2.SCROLL_STATE_DRAGGING && currentPosition == position && currentPosition == 0)
                  viewPager2.setCurrentItem(2);
              else if (myState == ViewPager2.SCROLL_STATE_DRAGGING && currentPosition == position && currentPosition == 2)
                  viewPager2.setCurrentItem(0);
      
              super.onPageScrolled(position, positionOffset, positionOffsetPixels);
          }
      
          @Override
          public void onPageSelected(int position)
          {
              currentPosition = position;
      
              super.onPageSelected(position);
          }
      
          @Override
          public void onPageScrollStateChanged(int state)
          {
              myState = state;
      
              super.onPageScrollStateChanged(state);
          }
      });
      

      【讨论】:

      • 这段代码节省了我一天的精力。尽管我一直在寻找其他东西,但使用这个逻辑我们可以限制多个 onPageScrolled 调用。
      【解决方案4】:

      我编写了一个准备三个页面的程序,当您从中心页面移动时,页面的内容会移动并返回到中心页面。如果对你有用,我很高兴。

      (我只有4个月的编程经验,所以这段代码仅供参考)

      在您的片段或活动中

      ・・・
      private ViewModel viewModel;
      private ViewPager2 pager2;
      private List<Integer> numbers;
      private int currentPosition;
      
      public View onCreateView(@NonNull LayoutInflater inflater,
                               ViewGroup container, Bundle savedInstanceState) {
          ・・・
          pager2.setCurrentItem(1, false);
      
          ・・・
      }
      private ViewPager2.OnPageChangeCallback pageChangeCallback = new ViewPager2.OnPageChangeCallback() {
          @Override
          public void onPageSelected(final int position) {
              super.onPageSelected(position);
              currentPosition = position;
          }
      
          @Override
          public void onPageScrollStateChanged(final int state) {
              super.onPageScrollStateChanged(state);
              //If you moved from center page, repack the contents of the pages and go back to center page.
              if(state == pager2.SCROLL_STATE_IDLE && currentPosition != 1){
                  items = viewModel.shiftNumbers(numbers, 2-currentPosition, (currentPosition-1)*3);
                  adapter.notifyDataSetChanged();
                  pager2.setCurrentItem(1, false);
              }
          }
      };
      
      @Override
      public void onDestroyView() {
          super.onDestroyView();
          binding = null;
      }
      

      在您的视图模型中

      private List<Integer> numbers;
      
      public ViewModel() {
      
          numbers = new ArrayList<>();
      
          for (int i = 0; i < 3; i++) {
              numbers.add(i, i-1);
          }
      }
      public List<Integer> getNumbers(){
      return numbers;
      }
      
      public List<Integer> shiftNumbers (List<Integer> numbers, int pickPosition, int difference){
         //When moving to page 0 of [0,1,2], the contents of the page 2 are replaced with the contents that should be on page -1. Moving to page 2 is handled in the same way.
          int number = numbers.get(pickPosition);
          number = number+difference;
          //Then repack.
          numbers.remove(pickPosition);
          numbers.add(2-pickPosition,number);
          return numbers;
      }
      

      Adapter 和 ViewHolder 可以作为默认值。 当您也想显示上一页/下一页时,可以应用此功能。但是使用 PageTransformer 会有点困难。

      【讨论】:

        【解决方案5】:

        你应该在滚动完成后返回 view_pager 的第一个或最后一个位置。

        看到这个问题 - Changing ViewPager to enable infinite page scrolling

        【讨论】:

        • 非常感谢您的评论,但您提供的链接是 ViewPager 的,我需要 ViewPager2 适配器。 (它基本上是一个recyclerview适配器)
        【解决方案6】:

        定义一个扩展 ViewPager2.OnPageChangeCallback 的类:

        class ViewPager2PageChangeCallback(private val listener: (Int) -> Unit) :
                ViewPager2.OnPageChangeCallback() {
        
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                when (position) {
                    0 -> listener.invoke(5). // 0th element is a fake element in your list. When 0th element is opened(from 1 to 0) automatically open last element(5th index)
                    6 -> listener.invoke(1). // 6th element is a fake element in your list. When 6th element is opened(from 5 to 6) automatically open first element(1th index)
                }
            }
        }
        

        然后在你的活动中创建一个实例。

        onCreate(){
            viewPager2PageChangeCallback = ViewPager2PageChangeCallback {
                viewPager2.post {
                    viewPager2.setCurrentItem(it, false)
                }
            }
            viewPager2.registerOnPageChangeCallback(viewPager2PageChangeCallback)
        }
        

        当 Activity 被销毁时从它注销。

        override fun onDestroy() {
            super.onDestroy()
            viewPager2.unregisterOnPageChangeCallback(viewPager2PageChangeCallback)
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-02
          • 2017-01-06
          • 2021-10-06
          • 2022-01-02
          相关资源
          最近更新 更多