34

我正在使用 ViewPager2 创建幻灯片。例如,幻灯片有 3 个项目,我想在活动打开时显示第二个项目。我使用setCurrentItem(int item, boolean smoothScroll)方法,但它不起作用,没有任何反应。我怎样才能实现它?

viewPager.adapter = adapter
viewPager.setCurrentItem(1, true)
4

13 回答 13

48

我认为更简单更可靠的解决方法是推迟到下一个运行周期而不是不安全的延迟,例如

viewPager.post {
  viewPager.setCurrentItem(1, true)
}
于 2020-03-30T14:53:07.843 回答
19

setCurrentItem(int item, boolean smoothScroll)可以正常工作,ViewPagerViewPager2不能按预期工作。setCurrentItem(int item, boolean smoothScroll最后,我通过将) 方法添加到这样的延迟中来面对这个问题:

Handler().postDelayed({
     view.viewPager.setCurrentItem(startPosition, false)
}, 100)
于 2019-05-26T11:42:05.587 回答
14

不要使用计时器,你会遇到很多可能的状态,其中用户的手机速度很慢,实际上运行时间超过 100 毫秒,而且,你不希望计时器太慢,让它变得荒谬不可靠。

下面我们执行以下操作,我们为 ViewTreeObserver 设置一个侦听器,并等到在 ViewPager2 的 RecyclerView 中布置了一定数量的子节点(它是内部工作的)。一旦我们确定已经布置了 x 个项目,我们就开始我们的无动画滚动以从该位置开始。

val recyclerView = (Your ViewPager2).getChildAt(0)

recyclerView.apply {
   val itemCount = adapter?.itemCount ?: 0
   if(itemCount >= #(Position you want to scroll to)) {
      viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
      override fun onGlobalLayout() {
         viewTreeObserver.removeOnGlobalLayoutListener(this)

         // False for without animation scroll
         (Your ViewPager2).scrollToPosition(#PositionToStartAt, false)
      }
   }
}
于 2019-08-15T21:24:30.157 回答
11

首先,我认为接受的答案不应该是@hosseinAmini 的,因为它建议使用延迟来解决问题。您应该首先寻找假定的错误是由什么引起的,而不是相信这样的不合理的解决方案。

@Rune 的提议是正确的,相反;所以我在回答中引用了他们的代码:

viewPager.post {
    viewPager.setCurrentItem(1, true)
}

我唯一要争论的是前面提到的人认为他们的解决方案只是在下一个运行周期中推迟执行该 lambda。这不会使任何错误的东西正常工作。相反,它实际上正在做的是将 lambda 的执行推迟到视图已附加到窗口后,这意味着它也已添加到父视图中。ViewPager2实际上,在附加到窗口之前更改当前项目似乎存在问题。支持这一说法的一些证据如下:

  • 使用Handler几乎不会有效的任何方法。

    Handler(Looper.getMainLooper()).post {
        viewPager.setCurrentItem(1, true) // Not working properly
    }
    

    从理论的角度来看,它可能由于ViewPager2附加到主循环器的消息队列中获取优先级的窗口而偶然起作用,但这不应该被依赖,因为不能保证它会起作用(它甚至更有可能不会),如果它甚至被证明是有效的,运行多个测试的进一步调查应该会让我的观点清楚。

  • View.handlergets null,这意味着该视图尚未附加到任何窗口。

    View.handler // = null
    

    尽管 Android UI 被绑定到主循环器,它总是唯一地对应于主线程 - 因此也称为 UI 线程 - 一个奇怪的设计选择在于处理程序不与每个视图相关联,直到它们附加到窗口。这可能是因为视图无法在主线程上调度任何工作,而它们不是层次结构的一部分,这在实现一个调度视图更新但不知道其生命周期的视图控制器时可能会变得有用(在在这种情况下,它会使用View' 处理程序,如果有的话——或者如果没有,就跳过调度程序。

编辑:

此外,@josias 在评论中指出使用起来会更清楚:

viewPager.doOnAttach {
    viewPager.setCurrentItem(1, true)
}

谢谢你的建议!它更好地表达了实际意图,而不是依赖于View.post方法的行为。

于 2020-09-09T16:04:19.427 回答
7

如上所述,您必须在ViewPager2setCurrentItem(position, smoothScroll)上使用方法才能显示所选项目。为了让它工作,你必须定义一个回调,这里是一个例子:

ViewPager2.OnPageChangeCallback callback = new ViewPager2.OnPageChangeCallback() {
    @Override
    public void onPageSelected(int position) {
        super.onPageSelected(position);
    }
};

然后你必须按如下方式注册它:

viewPager.registerOnPageChangeCallback(callback);

也不要忘记注销它:

viewPager.unregisterOnPageChangeCallback(callback);

当您调用setCurrentItem(position)方法时,它将onPageSelected(int position)从您的回调中调用方法并传递您的参数,然后将调用类中的方法createFragment(int position)FragmentStateAdapter显示您的片段

于 2020-03-10T19:45:26.937 回答
6

mViewPager.setCurrentItem(1, true); --->正如你在上面写的那样就足够了

毫无疑问,这应该可行,只需检查您的位置:

        @Override
        public void onPageSelected(int i) {
            if (LOG_DEBUG) Log.v(TAG, " ++++++++    onPageSelected:  " + i);
            mViewPager.setCurrentItem(i);
            //TODO You can use this position: to write other dependent logic

        }

并检查

PagerAdapter 中的 getItem(int position)

或者粘贴您的代码。

于 2019-05-26T08:54:22.157 回答
6

不要使用计时器和所有带有“post”的东西,这不是可靠的解决方案,只是一段有味道的代码。

相反,请尝试使用viewPager.setCurrentItem(1, false). 那个'假'是关于smoothScroll的,当你的活动刚刚打开时,你不能平滑滚动你的viewPager2。在 onViewCreated() 方法中的片段上对其进行了测试,它也不适用于“true”,但适用于“false”

于 2021-04-29T14:55:14.977 回答
5

我尝试更改 viewpager2 页面,Handler().dely()甚至viewPager2.post{}'viewPager2.get(0).post也没有为我工作,我正在使用ViewPagerwith FragmentStateAdapterwith Tablayout

对我有用的是在手动绑定后更改in 的位置RecylerViewViewPager2 FragmentStateAdapteryourViewPager2View.adapter

 (yourViewPager2View[0] as RecyclerView).scrollToPosition(moveToTabNumber)

为什么

我的问题是无论我设置的页面是什么pageNumber ,总是在0位置开始片段的onCreateFragment(position:Int):Fragmeet功能FragmentStateAdapter

viewPager.setCurrentItem = pageNumber

我检查了FragmentStateAdapter它的调用位置FragmentStateAdapter

onBindViewHolder(final @NonNull FragmentViewHolder holder, int position)`

所以我需要的只是用我想要的页码强制onBindViewHolder调用。onCreateFragment(position:Int)

于 2020-04-29T17:10:42.523 回答
2

首先,您需要在您想要的任何侦听器或按钮下初始化 Main 活动,然后您需要放置此行.. 这里 MainActvity 是您正在使用的 Viewpager 主类,2 是您要移动的位置

 MainActivity main = (MainActivity ) mContext;
    main.selectTab(2, true);
于 2019-05-27T05:15:00.243 回答
1

我遇到了同样的问题。在我的情况下,我默认使 viewPager2 消失,直到网络请求成功,我通过在使 viewPager2 可见后设置 CurrentItem 来修复它。

于 2021-03-25T12:50:26.817 回答
1

我的回答现在可能没有帮助,但我认为发布我的经验并没有什么害处,我只是使用 ViewPager 和 ViewPager2 遇到了这个问题,并通过更改一些行代码顺序意外地解决了它。

这是 ViewPager 的 (java) 解决方案:

    reviewWordViewPager.addOnPageChangeListener(changeListener);
    reviewWordViewPager.setCurrentItem(viewPosition, true/false);
    reviewWordTabIndicator.setupWithViewPager(reviewWordViewPager, true);

ViewPager2 的 (Java) 解决方案:

wordViewPager.registerOnPageChangeCallback(viewPager2OnPageChangeCallback);
wordViewPager.setCurrentItem(vpPosition, true/false);
new TabLayoutMediator(tabIndicator, wordViewPager,
            ((tab, position) -> tab.setText(viewPagerTitle[position]))).attach();

我没有查找 ViewPager2 是否需要 ViewPager 中使用的以下旧代码

    @Override
public int getItemPosition(@NonNull Object object) {
    // refresh all fragments when data set changed
    return POSITION_NONE;
}

但令人惊讶的是,在 ViewPager2 中不需要它来解决我一直遇到的问题,希望它可以帮助其他人

如果您用于context.startActivity启动新活动,则无需wordViewPager.setCurrentItem(item, smoothScroll)onResume函数中使用以在开始新活动之前返回最后选择的选项卡,您只需像vpPisition = wordViewPager.getCurrentItem();onStop函数中一样保存 ViewPager/ViewPager2 位置。

vpPisition是一个全局变量。

于 2021-09-28T13:54:02.943 回答
0

作为@Daniel Kim,但是一个java版本

RecyclerView rvOfViewPager2 = (RecyclerView) viewPager2.getChildAt(0);
    rvOfViewPager2.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
    {
        @Override
        public void onGlobalLayout()
        {
            rvOfViewPager2.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            viewPager2.setCurrentItem(currentTabId, false);
        }
    });
于 2021-12-08T18:13:14.827 回答
0

我注意到,如果您选择不为其设置动画,则在最初创建视图时它可以正常工作。

viewPager2.setCurrentItem(index, false)

根据您的用例,这通常很好 - 这个初始/默认项目可能不需要动画。

于 2021-12-22T00:30:40.790 回答