【问题标题】:Android ViewPager - multiple pageChangeListener not working properlyAndroid ViewPager - 多个 pageChangeListener 无法正常工作
【发布时间】:2017-02-23 09:16:59
【问题描述】:

当我尝试添加多个 pageChangleListener 时,ViewPager 出现了一个奇怪的问题。我尝试寻找具有类似描述的帖子/问题,但找不到任何内容,所以我们开始:

短版:第一页只有一个 onPageChangeListener 触发,但第二页触发了多个监听器。

长版: 在我的应用程序中,我需要每个 ViewPager 都有多个 OnPageChangeListeners。尽管我在 android 文档中找不到任何确认,但函数“addOnPageChangeListener”的名称表明我可以将多个侦听器设置为同一个 ViewPager(特别是因为它曾经是“setPageChangeListener”,他们将其更改为“addPageChangeListener”)。现在,我有一些代码需要在每次在我的应用程序的任何 ViewPager 中选择页面时运行,所以我创建了自己的 ViewPager 和 OnPageChangeListener 类:

public class MyViewPager extends ViewPager
{
    private ArrayList<Sequence> mSequenceList;

    public MyViewPager(Context context)
    {
        super(context);
    }

    public MyViewPager(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    @Override
    public void setAdapter(PagerAdapter adapter)
    {
        super.setAdapter(adapter);

        mSequenceList = ((MyPagerAdapter)adapter).getSequences();

        addOnPageChangeListener(new MyOnPageChangedListener(mSequenceList));
        Log.i("aaaaa", "added listener from MyViewPager");
    }
}

我还做了自己的页面更改监听器:

public class MyOnPageChangedListener implements MyViewPager.OnPageChangeListener
{
    private static final String TAG = "MyOnPageChangedListener";

    private ArrayList<Sequence> sequenceList;

    public MyOnPageChangedListener(ArrayList<Sequence> sequenceList)
    {
        this.sequenceList = sequenceList;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
    {
        //do nothing
    }

    @Override
    public void onPageSelected(int position)
    {
        Log.i("aaaaa", "selecting page " + position + " from my listener");

        //do something
    }

    @Override
    public void onPageScrollStateChanged(int state)
    {
        //do nothing
    }

我的活动的 OnCreate 中也有这个:

mPageChangedListener = new ViewPager.OnPageChangeListener()
        {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
            {
                //do nothing
            }

            @Override
            public void onPageSelected(int position)
            {Log.i("aaaaa", "selecting page " + position + " from activity");
                //do something
            }

            @Override
            public void onPageScrollStateChanged(int state)
            {
                //do nothing
            }
        };

        mViewPager.addOnPageChangeListener(mPageChangedListener);
        Log.i("aaaaa", "added listener from activity");

我在我的活动中使用它来触发第一页的 onPageSelected

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

    mViewPager.post(new Runnable()
    {
        @Override
        public void run()
        {
            mPageChangedListener.onPageSelected(mViewPager.getCurrentItem());
        }
    });
}

但是当我运行应用程序时,我得到了一些奇怪的行为。这是日志输出:

02-23 03:08:41.192 17147-17147/com.*** I/aaaaa: added listener from MyViewPager
02-23 03:08:41.193 17147-17147/com.*** I/aaaaa: added listener from activity
02-23 03:08:41.273 17147-17147/com.*** I/aaaaa: selecting page 0 from activity
02-23 03:09:03.609 17147-17147/com.*** I/aaaaa: selecting page 1 from my listener
02-23 03:09:03.609 17147-17147/com.*** I/aaaaa: selecting page 1 from activity

如您所见,添加了两个侦听器,但对于第一页(索引 0),仅触发来自活动的侦听器,而对于第二页(索引 1),两个侦听器都被触发。

这对我来说是一个主要问题,因为我自己的 onPageChangedListener 必须为每个页面触发

有人知道为什么会发生这种情况以及如何解决吗?

【问题讨论】:

    标签: android android-viewpager


    【解决方案1】:

    当然,在发布此问题 1 分钟后,我想通了……我正在为可能面临类似问题的任何人发布解决方案:

    问题在于我的活动的 onResume() 方法。我注意到我只触发了我在活动中注册的侦听器,而不是我的自定义侦听器。问题是我无法从活动中访问 MyOnPageChangedListener,所以我将它设置为 MyOnPageChangedListener 中的全局变量并添加了一个 get 方法来访问它。这是我修改后的课程:

    public class MyViewPager extends ViewPager
    
    {
        private ArrayList<Sequence> mSequenceList;
    
        private MyOnPageChangedListener mPageChangeListener;
    
        //making the compiler happy
        public MyViewPager(Context context)
        {
            super(context);
        }
    
        //making the compiler happy
        public MyViewPager(Context context, AttributeSet attrs)
        {
            super(context, attrs);
        }
    
        @Override
        public void setAdapter(PagerAdapter adapter)
        {
            super.setAdapter(adapter);
    
            mSequenceList = ((MyPagerAdapter)adapter).getSequences();
    
            mPageChangeListener = new MyOnPageChangedListener(mSequenceList);
            addOnPageChangeListener(mPageChangeListener);
            Log.i("aaaaa", "added listener from MyViewPager");
        }
    
        public MyOnPageChangedListener getMyPageChangeListener()
        {
            return mPageChangeListener;
        }
    }
    

    这是我更新的 onResume:

    @Override
    protected void onResume()
    {
        super.onResume();
    
        invalidateOptionsMenu();
    
        //this is to invoke "onPageSelected()" for the first page
        //when the viewpager is first created, and when the activity is resumed
        //(for example when coming back from "edit profile")
        mViewPager.post(new Runnable()
        {
            @Override
            public void run()
            {
                mViewPager.getMyPageChangeListener()
                          .onPageSelected(mViewPager.getCurrentItem());
                mPageChangedListener.onPageSelected(mViewPager.getCurrentItem());
            }
        });
    }
    

    然而,这对我来说有点像一个 hack,而不是一个优雅的解决方案......这也意味着我必须为每一个想要触发我自己的监听器的活动手动执行此操作。

    很遗憾没有“getAllPageChangedListeners”这样的方法。

    有没有更好的解决方案?

    【讨论】:

      【解决方案2】:

      由于您仅在 mPageChangedListener 上显式调用 onPageSelected(),因此它不会经历选择页面的完整周期,因此不会为分配的所有侦听器调用 onPageSelected()。 如果您查看 ViewPager 源代码,您可以看到如果您触发,则不会调用 dispatchOnPageSelected(int position)

      mPageChangedListener.onPageSelected(mViewPager.getCurrentItem());
      

      就像你做的那样。

      我错过了 setCurrentItem(int position) 如果所选项目已被选择则无效的事实......所以对于初始选择,您应该保留所有侦听器的实例并在每个侦听器上调用 onPageSelected()。 之后,使用 mViewPager.setCurrentItem(int position) 将触发您的两个(或所有)听众。

      【讨论】:

      • 是的,我在发布问题后几乎立即注意到了这一点,哈哈(见我的回答)。不幸的是,虽然您的解决方案应该有效,但它却没有:当我调用“setCurrentItem(mViewPager.getCurrentItem())”时,它不会触发“onPageSelected”方法(在任何侦听器中)。跨度>
      • 对,我错过了默认选中的项目为0,因此再次选择它没有效果。编辑了答案,解决方案几乎是您自己的答案:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-29
      • 1970-01-01
      • 2016-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多