onPageSelected在第一页不起作用

62

我的页面适配器(扩展PagerAdepter)中有一个textview。我使用MainActivity的onPageSelected更新这个textview。它会为位置大于0的页面更新textview,但是起始页面(位置0)在启动时不会更新。当我向前滑动再向后滑动时,它会设置位置0的值,但我无法在启动时设置值。如何设置起始页面的值?

public void onPageSelected(int position) {

             switch (position) {
                case 0: 

                     PagerAdapter.tv.setText("value"); // => doesnt work on start

                    break;

                            default:
                                   PagerAdapter.tv.setText("value");
                                         break;
}
}

完整的代码:

public class QuizStart extends SlidingFragmentActivity {
 @Override
        protected void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

           this.setContentView(R.layout.activity_main);

              mPager = (ViewPager)findViewById(R.id.pager);         
            QuestionPagerAdapter mAdapter = new QuestionPagerAdapter();

            mPager.setAdapter(mAdapter);
            QuizStart.onPageSelected(0);

         mPager.setOnPageChangeListener(new OnPageChangeListener() {


            @Override
            public void onPageScrollStateChanged(int arg0) { }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) { }

            @Override
            public void onPageSelected(int position) {

             switch (position) {
                case 0: 

                     PagerAdapter.tv.setText("value"); // => doesnt work on start

                    break;

                            default:
                                   PagerAdapter.tv.setText("value");
                                         break;
                }
            }

        });
}
}

你的PageAdapter为什么要包含一个View?这听起来有些可疑。你想要实现什么样的功能? - dmon
我从数据库获取数据并将其设置到paderadapters的textview中。 - metemet06
10个回答

82

当您创建OnPageChangeListener时,可以手动调用 onPageSelected(0)来设置起始状态:

OnPageChangeListener pageChangeListener = new OnPageChangeListener() {
    @Override
    public void onPageScrollStateChanged(int arg0) { }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) { }

    @Override
    public void onPageSelected(int position) {

        switch (position) {
        case 0: 
            QuizStart.next.setText(getmycurrentpage()+"");
            break;

        default:

            QuizStart.next.setText(getmycurrentpage()+"");
            break;
        }
    }
});

   mPager.setOnPageChangeListener(pageChangeListener);
   // do this in a runnable to make sure the viewPager's views are already instantiated before triggering the onPageSelected call
   mPager.post(new Runnable()
   {
       @Override
       public void run() 
       {
           pageChangeListener .onPageSelected(viewPager.getCurrentItem());
       }
   });

编辑

现在它不会因为@metemet06指出的空指针异常而发生。


是的,我使用OnPageChangeListener。我的活动包含:mPager.setOnPageChangeListener(new OnPageChangeListener(){... public void onPageSelected ... - metemet06
3
谢谢您的帮助。但是我仍然遇到了空指针异常。我认为即使在设置适配器后我们使用它,Pageradapter 中的 textview 还没有准备好。 - metemet06
25
如果您手动调用onPageSelected,那么这是不正确的,因为翻页器/适配器可能还没有准备好,正如metemet06所指出的那样,您将会得到一个空指针异常。 - Soham
1
@Soham 绝对正确。Fragment 尚未附加到其父级,如果您的代码依赖于 Fragment 已附加 (getParentFragment() 或 getActivity()),它将崩溃。还有其他人有什么想法吗? - mtmurdock
这很聪明,非常有帮助! - waseefakhtar
显示剩余9条评论

59
对我来说,下一个代码可以工作。
    viewPager.addOnPageChangeListener(myOnPageChangeListener);

.

    ViewPager.OnPageChangeListener myOnPageChangeListener = new ViewPager.OnPageChangeListener() {

    //declare key
    Boolean first = true;

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (first && positionOffset == 0 && positionOffsetPixels == 0){
            onPageSelected(0);
            first = false;
        }
    }

    @Override
    public void onPageSelected(int position) {
        //do what need
    }

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

4
我的解决方案是扩展Pager Adapter并在其中创建一个接口。然后,让适配器仅在创建适配器后调用接口一次。在接口回调中,您可以调用onPageChanged方法而不会出现nullpointerexception。将此类添加到您的项目中,并从中扩展您的适配器。在将适配器设置为ViewPager之前不要忘记将监听器设置为适配器。适配器类如下:
public abstract class ExtendedPagerAdapter extends FragmentPagerAdapter {

private boolean instantiated;
private AdapterListener adapterListener;

public interface AdapterListener {
    void onAdapterInstantiated();
}

public ExtendedPagerAdapter(FragmentManager fragmentManager) {
    this(fragmentManager, null);
}

public ExtendedPagerAdapter(FragmentManager fragmentManager, AdapterListener adapterListener) {
    super(fragmentManager);
    this.adapterListener = adapterListener;
    instantiated = false;
}

@Override
public void finishUpdate(ViewGroup container) {
    super.finishUpdate(container);
    if (!instantiated) {
        instantiated = true;
        if (adapterListener != null) {
            adapterListener.onAdapterInstantiated();
        }
    }
}

public void setAdapterInstantiatedListener(AdapterListener adapterListener) {
    this.adapterListener = adapterListener;
}
}

这是个好主意,在我的情况下完美运作!我想再花点时间调查一下,是否在片段已附加到活动后实际上调用了 finishUpdate - Entreco
1
根据Google文档显示,FragmentPagerAdapter.finishUpdate方法在显示页面发生改变时被调用。因此,每次片段发生变化时都会调用finish update。另外,如果我们深入源代码,可以看到这个方法会提交并执行所有未完成的事务 :) - Arūnas Bedžinskas

1

实际上,我找到了一种基于答案的好方法。

问题在于当设置监听器时,ViewPager没有完全加载,第一页被调用,但是ViewPager内部的片段尚未完全初始化。

为了解决这个问题,我只需保留一个变量来知道是否已经调用了第一页:

private boolean mFirstPageCalled = false;

然后在监听器中:

@Override
public void onPageSelected(int position) {

    if(mFirstPageCalled) {
        // Do your thing
    } else {
        mFirstPageCalled = true;
    }
}

最后要做的是处理被忽略的第一页,这可以在ViewPager完全创建后完成:
    mViewPager.post(new Runnable(){
        @Override
        public void run() {
            // Do your thing
        }
    });

在我的情况下,我只是在这里回调当前显示的片段并对其进行操作:
Fragment currentFragment = mPagerAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem());
currentFragment.customAction();

post方法只被调用一次,因此非常适合处理第一页的情况。


1
我通过在我的活动的onCreate()方法中添加应该执行的第一页的代码来解决了我的应用程序中的问题。这不是最美观的hack,涉及一些冗余代码,但由于我只想在页面更改时加载一些文本数据,所以这是我情况下最简单的方法。
手动调用onPageSelected()会导致NullPointerException,就像上面其他评论中描述的那样。

0

我的解决方案是扩展ViewPager

class MyViewPager extends ViewPager {

    private List<OnPageChangeListener> _Listeners = new ArrayList<>();

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

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

    @Override
    public void addOnPageChangeListener(OnPageChangeListener listener) {
        super.addOnPageChangeListener(listener);
        _Listeners.add(listener);
    }

    @Override
    public void removeOnPageChangeListener(OnPageChangeListener listener) {
        super.removeOnPageChangeListener(listener);
        _Listeners.remove(listener);
    }

    @Override
    public void setCurrentItem(final int item) {
        if (item != getCurrentItem()) {
            super.setCurrentItem(item);
        } else {
            post(new Runnable() {
                @Override
                public void run() {
                    dispatchOnPageSelected(item);
                }
            });
        }
    }

    private void dispatchOnPageSelected(int position) {
        for (int i = 0, size = _Listeners.size(); i < size; i++) {
            OnPageChangeListener listener = _Listeners.get(i);
            if (listener != null) {
                listener.onPageSelected(position);
            }
        }
    }
}

0

因为我按照这里建议的做法,但并没有起作用,所以我会分享一下对我有效的方法,就是在onPageScrolled中添加实例化函数,像这样:

   @Override
   public void onPageScrolled(int arg0, float arg1, int arg2) {

           adapter.closeText(arg0, viewPager);

    }

可能它能够工作的原因是onPagesScrolled在Android任务管理器的行下方被激活,因此我们通过跳过将监听器附加到视图来防止页面充气时出现不需要的情况。


0

我觉得你真正想做的是重写getPageTitle(),然后在其他地方获取该页面标题并将其设置为TextView的文本。

这样当您初始化活动、初始化PagerAdapter并调用getTitle(0)时,这将为您获取第0页的标题。现在将其设置为Activity的textview。稍后,每当您更改页面时,只需调用getTitle(newPosition)并更新该TextView即可。


0

你可能使用了一些AsyncTasks来加载数据集,很难确定哪个任务会最后完成。只有在最后一个任务完成后,整个数据集才能完成,然后才能调用OnPageSelected函数,因为它会在整个数据集上刷新UI。

与其在页面第一次加载时手动调用OnPageSelected函数,不如按照“正常”的方式逐步构建UI。稍后,当用户更改页面时,数据集应该是完整的,可以安全地让更新在OnPageSelected函数中进行。


0
@Override
public void addOnPageChangeListener(@NonNull OnPageChangeListener listener) {
    super.addOnPageChangeListener(listener);
    if (getAdapter() != null && getAdapter().getCount() > 0) {
        post(() -> listener.onPageSelected(getCurrentItem()));
    }
}

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