调用setCurrentItem(0)时不会触发onPageSelected

jpi*_*ihl 61 android android-viewpager

我有一个带有ViewPager的Activity,它显示了一堆图片.启动时,ViewPager的位置根据用户在上一个Activity中选择的内容进行设置.类似于画廊.

我希望每次选择新页面时调用onPageSelected,即首次打开活动时或用户滑动到新页面时.

我设置起点如下:

 mPager.setCurrentItem(index);
Run Code Online (Sandbox Code Playgroud)

一切正常,除非在将index设置为0的情况下调用setCurrentItem,因为这不会触发onPageSelected.

mPager.setOnPageChangeListener(new OnPageChangeListener() {
  @Override
  public void onPageSelected(int index) {
    Log.d(TAG, "onPageSelected " + index);
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是; 这是一个错误,如果是这样,我该怎么办呢?

tec*_*law 81

到目前为止我发现的最干净的解决方案是引用你在ViewPager上设置的onPageChangeListener(因为我认为没有ViewPager.getOnPageChangeListener()方法),然后你设置了ViewPager的适配器之后,致电:

onPageChangeListener.onPageSelected(viewPager.getCurrentItem());
Run Code Online (Sandbox Code Playgroud)

但是,当前索引处的页面片段尚未实例化(至少如果您使用的是FragmentStatePagerAdapter),因此您可能需要将其包装在runnable中,ala:

viewPager.post(new Runnable(){
@Override
    public void run() {
        onPageChangeListener.onPageSelected(viewPager.getCurrentItem());
    }
});
Run Code Online (Sandbox Code Playgroud)

此外,如果在onPageSelected处理程序中需要对片段的引用,则必须自己执行此操作.我为FragmentStatePagerAdapter使用了一个抽象基类,它覆盖了实例化和销毁方法,并添加/删除了SparseArray中的片段.

  • 发布runnable只是我需要确保viewPager的视图在触发onPageSelected调用之前已经实例化的秘诀.太好了! (6认同)

jpi*_*ihl 34

好的,所以我无法弄清楚这是不是一个bug(或一个功能).但我想我会分享一个可能解决问题的方法.

在Activity中的方法中编写要执行的功能,然后在onPageSelected方法中调用它.

mPager.setOnPageChangeListener(new OnPageChangeListener() {
    @Override
    public void onPageSelected(int index) {
        myOnPageSelectedLogic(index);
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后在打电话后

setCurrentItem(index);
Run Code Online (Sandbox Code Playgroud)

在Activity中,添加以下if语句

if(index == 0) {
    myOnPageSelectedLogic(0);
}
Run Code Online (Sandbox Code Playgroud)

这不是超级漂亮,但我希望它可以帮助别人:)

  • 更好的解决方案是调用 setCurrentItem(index); 里面 view.post(new Runnable(){ setCurrentItem(index); } ); 此代码保证 OnPageChangeListener 将被调用。至少在我的情况下 (3认同)
  • 实际上这是一个糟糕的方法,因为 OnPageChangeListener 在一些异步工作之后被调用,并且使用这个“技巧”在很多情况下你将无法正常工作 (2认同)

小智 23

其实我认为这是一个BUG.我已经检查ViewPager的源代码,发现唯一的触发使用onPageSelected:

if (dispatchSelected && mOnPageChangeListener != null) {
            mOnPageChangeListener.onPageSelected(item);
        }
Run Code Online (Sandbox Code Playgroud)

上面,变量dispatchSelected由代码确定:

final boolean dispatchSelected = mCurItem != item; 
//item is what you passed to setCurrentItem(item)
Run Code Online (Sandbox Code Playgroud)

并且,变量mCurItem被定义并初始化为:

private int mCurItem; // Index of currently displayed page.
Run Code Online (Sandbox Code Playgroud)

因此mCurItem默认为0.调用setCurrentItem(0)时,dispatchSelected将为false,因此不会调度onPageSelected().

现在我们应该理解为什么调用setCurrentItem(0)是一个问题而setCurrentItem(1或2,3 ...)不是.

如何解决这个问题呢:

  1. 将ViewPager的代码复制到您自己的项目并修复它:

    from 
    final boolean dispatchSelected = mCurItem != item; //the old line
    to
    final boolean dispatchSelected = mCurItem != item || mFirstLayout; //the new line
    
    Run Code Online (Sandbox Code Playgroud)

    然后使用您自己的ViewPager.

  2. 由于您有意识地调用了setCurrentItem(0)并且对OnPageChangedListener有了参考,因此请自行调度onPageSelected().

  3. 听楼下怎么说...

  • 很好找.你有没有报道过这个问题? (2认同)