onResume后,Android ViewPager setCurrentItem无法正常工作

时间:2013-10-11 11:12:36

标签: android methods android-viewpager onresume

我遇到了这个奇怪的问题,ViewPager的setCurrentItem(position,false)工作得非常好,然后我切换到另一个活动,在回到第一个活动后,ViewPager总是在第一个项目上结束。即使我已将setCurrentItem添加到onResume方法,它仍然会忽略它。 当我试图将项目设置为越界索引时,它甚至没有抛出任何异常。 虽然稍后当我调用此方法时,单击按钮“next”时,它会按预期工作。 检查我的代码10次,以获得对setCurrentItem(0)或smth的任何可能调用,但它根本就没有。

19 个答案:

答案 0 :(得分:58)

我无法真正回答为什么会发生这种情况,但如果你将setCurrentItem调用延迟几毫秒,它应该可以工作。我的猜测是因为在onResume期间还没有渲染过程,而ViewPager需要一个或类似的东西。

private ViewPager viewPager;

@Override
public void onResume() {
    final int pos = 3;
    viewPager.postDelayed(new Runnable() {

        @Override
        public void run() {
            viewPager.setCurrentItem(pos);
        }
    }, 100);
}

更新:故事时间

所以今天我遇到了viewpager忽略了我的setCurrentItem操作的问题,我在stackoverflow中搜索了一个解决方案。我找到了一个有同样问题和解决方法的人;我实现了修复,它没有工作。哇!回到stackoverflow来downvote那个faux-fix-provider,并且......

是的,是我。我实现了我自己的错误的非修复,我第一次偶然发现问题(后来被遗忘)。我现在不得不低估自己提供不良信息。


我最初的“修复”工作的原因不是因为“渲染过程”;问题是寻呼机的内容是由微调器控制的。旋转器和寻呼机状态都在onResume上被恢复,因此旋转器onItemSelected侦听器在下一个事件传播周期中被调用,它确实重新填充了viewpager - 这次使用了不同的默认值。 在初始状态恢复期间删除和重置监听器解决了问题。

上面的修复工作是第一次,因为它在 onItemSelected事件触发后设置了寻呼机当前位置。后来,它由于某种原因停止工作(可能应用程序变得太慢 - 在我的实现中我没有使用100毫秒,但10毫秒)。然后我在清理周期中删除了postDelayed,因为它没有改变已经存在的错误行为。

更新2:我不能用自己的帖子投票。我认为,尊贵的seppuku是唯一的选择。

答案 1 :(得分:22)

我的活动OnCreate中有类似的问题。 适配器设置正确的计数和我 将适配器设置为后应用setCurrentItem 然而,ViewPager会返回超出范围的索引。我认为ViewPager在我设置当前项目时没有加载我的所有片段。通过在ViewPager上发布runnable,我能够解决这个问题。这是一个带有一点上下文的例子。

    // Locate the viewpager in activity_main.xml
    final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);

    // Set the ViewPagerAdapter into ViewPager
    viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));

    viewPager.setOffscreenPageLimit(2);

    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setCurrentItem(ViewPagerAdapter.CENTER_PAGE);
        }
    });

答案 2 :(得分:11)

我找到了一个非常简单的解决方法:

    if (mViewPager.getAdapter() != null)
        mViewPager.setAdapter(null);
    mViewPager.setAdapter(mPagerAdapter);
    mViewPager.setCurrentItem(desiredPos);

而且,如果这不起作用,你可以把它放在一个处理程序中,但是不需要定时延迟:

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mViewPager.setCurrentItem(desiredPos);
            }
        });

答案 3 :(得分:3)

我有同样的问题,我编辑

@Override
public int getCount() { return NUM_PAGES; }

我将NUM_PAGES设为错误仅为1。

答案 4 :(得分:3)

我在代码中遇到了类似的错误,问题是我在更改数据之前设置了位置。

解决方案只是稍后设置位置并通知数据已更改

notifyDataSetChanged()
setCurrentItem()

答案 5 :(得分:2)

FragmentActivity中的现代方法是在ViewPager.setcurrentItem(Int)的上下文中在协程中调用Dispatchers.Main函数:

lifecycleScope.launch(Dispatchers.Main) {
   val index = 1
   viewPager.setCurrentItem(index)
}

答案 6 :(得分:1)

我已经使用了这里描述的post()方法,并且确定它在某些情况下工作得很好但是因为我的数据来自服务器,所以它不是圣杯。

我的问题是我想拥有

notifyDataSetChanged

在任意时间调用,然后在我的viewPager上切换标签。所以在通知电话之后我就有了这个

ViewUtilities.waitForLayout(myViewPager, new Runnable() {
    @Override
    public void run() {
        myViewPager.setCurrentItem(tabIndex , false);
    }
});

public final class ViewUtilities {
    public static void waitForLayout(final View view, final Runnable runnable) {
        view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //noinspection deprecation
                view.getViewTreeObserver().removeGlobalOnLayoutListener(this);

                runnable.run();
            }
        });
    }
}

有趣的事实:最后的 // noinspection弃用是因为在API 16之后修复了API中的拼写错误,因此应该阅读remove On GlobalLayoutListener而不是removeGlobal On LayoutListener

这似乎涵盖了我的所有案例。

答案 7 :(得分:1)

针对那些试图在onCreate的{​​{1}}中设置当前项而又没有Activity“解决方案”的人的解决方案(在Kotlin中使用ViewModel等):

Runnable

显然,这将执行正确的操作顺序,而不会受到class MyActivity : AppCompatActivity() { lateinit var mAdapter: MyAdapter lateinit var mPager: ViewPager // ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.fragment_pager) // ... mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) mAdapter = MyAdapter(supportFragmentManager) mPager = findViewById(R.id.pager) mainViewModel.someData.observe(this, Observer { items -> items?.let { // first give the data to the adapter // this is where the notifyDataSetChanged() happens mAdapter.setItems(it) mPager.adapter = mAdapter // assign adapter to pager mPager.currentItem = idx // finally set the current page } }) 或延迟的干扰。

为完整起见,通常像这样实现适配器的Runnable(在这种情况下为setItems()):

FragmentStatePagerAdapter

答案 8 :(得分:1)

有人在这里的论坛上写道。 https://github.com/ionic-team/ionic/issues/5701为我工作

MyFunction

答案 9 :(得分:1)

对我来说,设置适配器

后设置了当前项目
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));

viewPager.setCurrentItem(idx);

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

答案 10 :(得分:0)

我这样做是为了恢复当前项目:

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

答案 11 :(得分:0)

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

这就是为什么我没有看到任何变化的原因。这就是为postDelayed()提供帮助的原因。

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

实用解决方案:我不知道一个稳定而简单的解决方案。我们应该能够检查该类是否即将重新创建它的视图,如果是这种情况,则将setCurrentItem()的调用推迟到onCreateView()

的末尾

答案 12 :(得分:0)

清洁简单 调用notifyDataSetChanged()后,无需在setCurrentItem中添加post方法。

答案 13 :(得分:0)

这是一个生命周期问题,正如这里的几张海报所指出的那样。但是,我发现发布Runnable的解决方案不可预测且可能容易出错。这似乎是一种通过将其发布到未来来忽略该问题的方法。

我并不是说这是最好的解决方案,但它确实可以在不使用Runnable的情况下运行。我在Fragment内有ViewPager的单独整数。 当下一个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); 
    }


}

答案 14 :(得分:0)

我在这个问题上工作了一个星期,我意识到发生这个问题是因为我在视图分页器片段中使用家庭活动上下文,并且只有在将其附加到活动之后,我们才能在片段中使用上下文。
创建视图分页器时,活动仅附加到第一页(0)和第二页(1)。当您打开第二页时,将附加第三页,依此类推!当您使用setCorrentItem()方法且参数大于1时,它想在附加页面之前打开该页面,因此该页面片段中的上下文将为null,应用程序将崩溃!因此,当您延迟setCorrentItem()时,它会起作用!首先,它被附加,然后将打开页面...

答案 15 :(得分:0)

我使用dsalaj代码作为参考。如有必要,我将与完整的解决方案共享代码。

我也强烈建议使用ViewPager2

解决方案

两种情况都必须放在Observer {}内:

  1. 第一种情况:仅在拥有第一个数据集时才初始化适配器,而不是在初始化之前,因为这会在页面调度中产生不一致。 我们必须将其作为适配器的参数传递给第一个数据集

  2. 第二种情况:从可观察到的第一次更改开始,我们将不得不从第二个数据集开始,只有我们已经初始化时,才必须通过公共方法将其传递给适配器具有第一个数据集的适配器。

GL

答案 16 :(得分:0)

可以使用ViewTreeObserver摆脱静态延迟。 随意使用Kotlin扩展程序作为简洁的选择。

view_pager.doOnPreDraw {
    view_pager.currentItem = 1
}

请确保您具有gradle依赖项:实现'androidx.core:core-ktx:1.3.2'

答案 17 :(得分:0)

我对不相关的选项卡调用onActivityCreated()感到困惑@Mahdi Arabpour对我来说是大开眼界的:)

对我来说,问题是当我单击第二个选项卡时,第三页(如上面的@Mahdi Arabpour所述)正在重建,等等,并且它丢失了其数据适配器,再次在o​​nActivityCreted中设置它可以解决我的问题:

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

答案 18 :(得分:-2)

你需要在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);
}