Android ViewPager在onResume后setCurrentItem无效

47
我遇到了一个奇怪的问题,ViewPagersetCurrentItem(position, false)方法一开始运行得很好,但是当我切换到另一个活动后,再回到这个活动时,ViewPager总是最终停留在第一个项目上。即使我将setCurrentItem添加到onResume方法中,它仍然会忽略它。当我尝试将项目设置为超出边界索引时,甚至没有抛出任何异常。 但是之后当我调用这个方法,当“下一个”按钮被点击时,它就像预期的那样工作。 我已经检查了我的代码10次,以寻找是否有任何可能调用setCurrentItem(0)或类似的东西,但根本没有相关代码。

1
任何代码片段都可以。 - imthegiga
1
我曾经遇到过类似的问题,我只是将设置当前项的代码移动到了设置适配器之后。我的意思是确保你先设置适配器,然后再设置位置。 - hazimdikenli
19个回答

2
在设置适配器后设置当前项对我起作用。
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));

viewPager.setCurrentItem(idx);

pagerSlidingTabStrip.setViewPager(viewPager);// assign viewpager to tabs

2
这是一个生命周期问题,正如几位发帖人所指出的那样。然而,我发现通过发布Runnable的解决方案不可预测且可能容易出错。这似乎是通过将问题发布到未来来忽略问题的一种方式。
我并不是说这是最好的解决方案,但它绝对可以在不使用Runnable的情况下正常工作。我在具有ViewPagerFragment内部保留了一个单独的整数。当下次调用onResume时,这个整数将保存我们要设置为当前页面的页面。该整数的值可以在任何时候设置,因此可以在FragmentTransaction之前或恢复活动时设置。还请注意,所有成员都是在onResume()中设置的,而不是在onCreateView()中设置的。
public class MyFragment extends Fragment
{
    private ViewPager           mViewPager;
    private MyPagerAdapter      mAdapter;
    private TabLayout           mTabLayout;
    private int                 mCurrentItem = 0; // Used to keep the page we want to set in onResume().

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.my_layout, container, false);
        mViewPager = (ViewPager) view.findViewById(R.id.my_viewpager);
        mTabLayout = (TabLayout) view.findViewById(R.id.my_tablayout);
        return view;
    }

    @Override
    public void onResume()
    {
        super.onResume();

        MyActivity myActivity = (MyActivity) getActivity();
        myActivity.getSupportActionBar().setTitle(getString(R.string.my_title));

        mAdapter = new MyPagerAdapter(getChildFragmentManager(), myActivity);
        mViewPager.setAdapter(mAdapter);
        mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
        mViewPager.setCurrentItem(mCurrentItem); // <-- Note the use of mCurrentItem here!
        mTabLayout.setupWithViewPager(mViewPager);
    }

    /**
     * Call this at any point before needed, for example before performing a FragmentTransaction.
     */    
    public void setCurrentItem(int currentItem)
    {
        mCurrentItem = currentItem;

        // This should be called in cases where onResume() is not called later,
        // for example if you only want to change the page in the ViewPager
        // when clicking a Button or whatever. Just omit if not needed.
        mViewPager.setCurrentItem(mCurrentItem); 
    }


}

在我的情况下,我将所有的初始化代码(例如设置适配器、监听器等)从onResume()方法中移动到onCreateView()方法中,这样就解决了问题。 - Variag

2
我曾经花了一周时间解决这个问题,发现问题出在我在视图分页片段中使用了主活动上下文。实际上,我们只能在片段附加到活动后才能使用片段上下文。当一个视图分页被创建时,活动仅会附加到第一页(0)和第二页(1)。当您打开第二页时,第三页就会附加,以此类推!当您使用setCurrentItem()方法并且参数大于1时,它想要在片段附加之前打开该页面,因此该页面片段中的上下文将为null,应用程序会崩溃!这就是为什么延迟setCurrentItem(),它可以工作的原因!首先它会被附加,然后才会打开页面... 最初的回答

1
我这样做是为了恢复当前项目:
@Override
protected void onSaveInstanceState(Bundle outState) {

    if (mViewPager != null) {
        outState.putInt(STATE_PAGE_NO, mViewPager.getCurrentItem());
    }

    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {

    if (savedInstanceState != null) {
        mCurrentPage = savedInstanceState.getInt(STATE_PAGE_NO, 0);
    }

    super.onRestoreInstanceState(savedInstanceState);
}

@Override
protected void onRestart() {
    mViewPager.setCurrentItem(mCurrentPage);
         super.onRestart();
}

问题是我没有恢复实例状态,只是调用了onStart和onResume方法。我的位置存储正确,而且我在调试模式下查看了我的ViewPager对象,它的mCurItem也被正确设置,但它仍然显示第一个元素。 - Maciej Boguta
我记不清了,但我想我遇到过类似的问题,并使用了上面的代码。但可能不完全相同。 - Szymon

1

我使用dsalaj代码作为参考。如果需要,我会分享完整解决方案的代码。

我还强烈推荐使用ViewPager2

解决方案

两种情况都必须在Observer {}中进行:

  1. 第一种情况:仅在我们拥有第一个数据集之后初始化适配器,而不是之前,因为这会导致分页不一致。 对于第一个数据集,我们必须将其作为适配器的参数传递

  2. 第二种情况:从可观察到的第一个更改开始,我们将从第二个数据集开始拥有必须通过公共方法传递给适配器的数据集,但前提是我们已经使用第一个数据集初始化了适配器。

祝好运


这在viewPager2上继续发生。太糟糕了。 - Gilberto Ibarra
我非常清楚地记得这对我兄弟起作用了。尝试调试关键点。然后评估适配器的初始化方式,如果可观察数据存在,则评估其如何设置新数据当可观察数据更新时。 - Braian Coronel

1

当我调用setCurrentItem()时,视图即将被重新创建。因此,实际上我是为viewpager调用setCurrentItem(),然后系统调用onCreateView(),从而创建一个新的viewpager。

这就是为什么我看不到任何变化的原因。这也是为什么postDelayed()可能有所帮助的原因。

理论解决方案:推迟setCurrentItem()的调用,直到视图被重新创建。

实际解决方案:我没有稳定且简单的解决方案。我们应该能够检查类是否即将重新创建其视图,如果是这种情况,则将setCurrentItem()的调用推迟到onCreateView()的末尾。


1

我对于onActivityCreated()在无关选项卡中被调用感到困惑,@Mahdi Arabpour的解释让我茅塞顿开 :)

对我来说,问题出在第三个页面(正如@Mahdi Arabpour所述)在我点击第二个选项卡时被重建,等等,导致它失去了数据适配器。在onActivityCreated()中重新设置它可以解决我的问题:

    if (myXXRecyclerAdapter != null) {
        myXXRecyclerAdapter = new MyXXRecyclerAdapter(myStoredData);
        mRecyclerView.setAdapter(myXXRecyclerAdapter );
        return;
    }

0
ViewPager2对我来说唯一有效的解决方案是:
mViewPager.setCurrentItem(index, false);

在这里,smoothScroll = false 是必须的!

如果我尝试在不指定 smoothScroll 参数的情况下调用:

mViewPager.setCurrentItem(index);

它没有工作。

-1

在 pager.setAdapter(buildAdapter()) 之后,你需要立即调用 pager.setCurrentItem(activePage)

@Override
public void onResume() {
    if (pager.getAdapter() != null) {
        activePage=pager.getCurrentItem();
        Log.w(getClass().getSimpleName(), "pager.getAdapter()!=null");
        pager.setAdapter(null);

    }

    pager.setAdapter(buildAdapter());            
    pager.setCurrentItem(activePage);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接