@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);

这样做效果很好。除了我不能改变内容。

So I found this: ViewPager PagerAdapter不更新视图

"我的方法是在instantiateItem()方法中对任何实例化的视图使用setTag()方法"

现在我想实现instantiateItem()来做到这一点。但我不知道我必须返回什么(类型是Object),与getItem(int position)的关系是什么?

我读了reference:

  • public abstract Fragment getItem (int position)

    返回与指定位置相关的片段。

  • public Object instantiateItem (ViewGroup container, int position)

    为给定的位置创建页面。适配器负责将视图添加到这里给定的容器中,尽管它只必须确保在它从finishUpdate(ViewGroup)返回时已经完成。

    容器 将显示该页面的包含视图。 position 要被实例化的页面位置。

    Returns

    返回一个代表新页面的对象。这不需要是一个视图,但可以是一些其他的页面的容器。

  • but I still don't get it.

    这是我的代码。我使用的是支持包V4。

    视图页测试

    public class ViewPagerTest extends FragmentActivity {
        private ViewPager pager;
        private MyFragmentAdapter adapter; 
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.pager1);
            pager = (ViewPager)findViewById(R.id.slider);
            String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};
            adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
            pager.setAdapter(adapter);
            ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    reload();
        private void reload() {
            String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
            //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
            adapter.setData(data);
            adapter.notifyDataSetChanged();
            pager.invalidate();
            //pager.setCurrentItem(0);
    

    我的碎片(MyFragmentAdapter

    class MyFragmentAdapter extends FragmentPagerAdapter {
        private int slideCount;
        private Context context;
        private String[] data;
        public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
            super(fm);
            this.slideCount = slideCount;
            this.context = context;
            this.data = data;
        @Override
        public Fragment getItem(int position) {
            return new MyFragment(data[position], context);
        @Override
        public int getCount() {
            return slideCount;
        public void setData(String[] data) {
            this.data = data;
        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
    

    我的碎片(MyFragment

    public final class MyFragment extends Fragment {
        private String text;
        public MyFragment(String text, Context context) {
            this.text = text;
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.slide, null);
            ((TextView)view.findViewById(R.id.text)).setText(text);
            return view;
    

    这里也有一个人有类似的问题,没有答案 http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html

    5 个评论
    为了更好地了解所涉及的组件,阅读我的关于带有独立的返回导航历史的标签式ViewPager的教程可能会有所帮助。它在GitHub上有一个工作实例和额外的资源。medium.com/@nilan/...
    你可以查看这个GitHub中的例子.我希望这个例子能帮助你。
    好的简单解决方案: stackoverflow.com/a/35450575/2162226
    谷歌最近推出了ViewPager2,简化了动态片段/视图的更新。你可以在以下网站上看到一些例子github.com/googlesamples/android-viewpager2或这个答案在stackoverflow.com/a/57393414/1333448
    android
    android-fragments
    android-viewpager
    User
    User
    发布于 2012-06-01
    21 个回答
    Bill Phillips
    Bill Phillips
    发布于 2020-04-09
    已采纳
    0 人赞同

    当使用FragmentPagerAdapter或FragmentStatePagerAdapter时,最好只处理getItem()而完全不碰instantiateItem()。PagerAdapter上的instantiateItem()-destroyItem()-isViewFromObject()接口是一个低级接口,FragmentPagerAdapter用来实现更简单的getItem()接口。

    在讨论这个问题之前,我应该澄清的是

    如果你想切换出正在显示的实际片段,你需要避免使用FragmentPagerAdapter而使用 FragmentStatePagerAdapter。

    这个答案的早期版本错误地使用了FragmentPagerAdapter作为例子--那是行不通的,因为FragmentPagerAdapter在第一次显示一个片段后从不销毁它。

    我不推荐你链接的帖子中提供的setTag()findViewWithTag()的解决方法。正如你所发现的,使用setTag()findViewWithTag()不能与片段配合,所以它不是一个好的配合。

    正确的解决方案是覆盖getItemPosition()。当notifyDataSetChanged()被调用时,ViewPager在其适配器中的所有项目上调用getItemPosition(),以查看它们是否需要被移动到不同的位置或移除。

    默认情况下,getItemPosition()会返回POSITION_UNCHANGED,意思是 "这个对象在那里很好,不要破坏或移除它。"返回POSITION_NONE可以解决这个问题,它说:"这个对象不再是我要显示的项目了,移除它吧。"所以它的效果是删除并重新创建你的适配器中的每一个项目。

    这是一个完全合法的修复!这个修复使得notifyDataSetChanged的行为像一个普通的Adapter一样,没有视图回收。如果你实现了这个修复,并且性能令人满意,你就可以去比赛了。工作完成了。

    如果你需要更好的性能,你可以使用一个更高级的getItemPosition()实现。下面是一个从字符串列表中创建片段的寻呼机例子。

    ViewPager pager = /* get my ViewPager */;
    // assume this actually has stuff in it
    final ArrayList<String> titles = new ArrayList<String>();
    FragmentManager fm = getSupportFragmentManager();
    pager.setAdapter(new FragmentStatePagerAdapter(fm) {
        public int getCount() {
            return titles.size();
        public Fragment getItem(int position) {
            MyFragment fragment = new MyFragment();
            fragment.setTitle(titles.get(position));
            return fragment;
        public int getItemPosition(Object item) {
            MyFragment fragment = (MyFragment)item;
            String title = fragment.getTitle();
            int position = titles.indexOf(title);
            if (position >= 0) {
                return position;
            } else {
                return POSITION_NONE;
    

    通过这个实现,只有显示新标题的片段会被显示。任何显示仍在列表中的标题的片段将被移动到它们在列表中的新位置,而标题不再在列表中的片段将被销毁。

    如果片段没有被重新创建,但还是需要更新怎么办?对一个活的片段的更新最好由片段本身处理。这就是拥有一个片段的好处,毕竟它是它自己的控制器。一个片段可以在onCreate()中为另一个对象添加一个监听器或一个观察者,然后在onDestroy()中移除它,从而自己管理更新。你不必像在ListView或其他AdapterView类型的适配器中那样把所有的更新代码放在getItem()中。

    最后一件事 - 仅仅因为FragmentPagerAdapter不销毁一个片段,并不意味着getItemPosition在FragmentPagerAdapter中完全无用。你仍然可以使用这个回调在ViewPager中重新排列你的片段。不过,它永远不会从FragmentManager中完全删除它们。

    User
    不起作用,它仍然没有更新任何东西......贴出了我的代码。
    好吧,想明白了问题所在 - 你需要使用 FragmentStatePagerAdapter 而不是 FragmentPagerAdapter。FragmentPagerAdapter永远不会从FragmentManager中删除片段,它只是分离和附加它们。查看文档以获得更多信息 - 一般来说,你只想对永久的片段使用 FragmentPagerAdapter。我已经编辑了我的例子并做了修正。
    User
    我将在以后的某个时间审查这个问题......谢谢你的回答。然后我接受你的答案,如果它是有效的。在你的最后一次评论之前,我实施了每次删除和添加一个新的ViewPager,这很有效。不是很好的解决方案,但我现在没有时间去改变它。
    User
    你是对的,把适配器的类改成FragmentStatePagerAdapter就解决了我所有的麻烦。谢谢!
    即使我返回POSITION_NONE,同时使用FragmentStatePagerAdapter,我可以看到它删除了我的片段并创建了新的实例。但是新的实例会收到一个带有旧片段状态的savedInstanceState Bundle。我怎样才能完全销毁这个片段,使Bundle在再次创建时为空?
    M-Wajeeh
    M-Wajeeh
    发布于 2020-04-09
    0 人赞同

    与其从getItemPosition()返回POSITION_NONE并导致全视图重现,不如这样做。

    //call this method to update fragments in ViewPager dynamically
    public void update(UpdateData xyzData) {
        this.updateData = xyzData;
        notifyDataSetChanged();
    @Override
    public int getItemPosition(Object object) {
        if (object instanceof UpdateableFragment) {
            ((UpdateableFragment) object).update(updateData);
        //don't return POSITION_NONE, avoid fragment recreation. 
        return super.getItemPosition(object);
    

    你的片段应该实现UpdateableFragment接口。

    public class SomeFragment extends Fragment implements
        UpdateableFragment{
        @Override
        public void update(UpdateData xyzData) {
             // this method will be called for every fragment in viewpager
             // so check if update is for this fragment
             if(forMe(xyzData)) {
               // do whatever you want to update your UI
    
    public interface UpdateableFragment {
       public void update(UpdateData xyzData);
    

    你的数据类。

    public class UpdateData {
        //whatever you want here
        
    这是否意味着你的MainActivity也需要实现这个接口呢?请帮助我解决这个问题。在我目前的实现中,我使用我的MainActivity类作为片段间的通信器,所以我对这个解决方案感到困惑。
    好吧,我已经在另一个地方回答了,我正在动态更新Fragment的完整实现,请看。stackoverflow.com/questions/19891828/...
    SERG
    @M-WaJeEh 请举个例子!!!我有一个包含城市列表的列表视图,在选择城市后,打开包含该城市信息的viewpager(3页)。
    在我过去2.5天的工作生活中看到的所有东西中......这是唯一一个对我有用的......各种感谢,谢谢,spaciba,sukran......。
    龙的母亲!,这工作就像一个魅力,唯一的解决方案对我来说是有效的......非常感谢!!!。
    Ziad Gholmish
    Ziad Gholmish
    发布于 2020-04-09
    0 人赞同

    当我有一个有7个片段的ViewPager时,对于那些仍然面临同样问题的人来说,这些片段默认从API服务中加载英文内容,但这里的问题是,我想从设置活动中改变语言,在完成设置活动后,我想让主活动中的ViewPager刷新片段,以符合用户的语言选择,并在用户选择阿拉伯语时加载阿拉伯语内容。 设置活动后,我想让主活动中的ViewPager刷新片段,以匹配用户的语言选择,如果用户选择了阿拉伯语,就加载阿拉伯语内容,这里是我第一次做的工作

    1- 你必须使用FragmentStatePagerAdapter,如上所述。

    2- 在mainActivity上,我覆盖了onResume,并做了以下工作

    if (!(mPagerAdapter == null)) {
        mPagerAdapter.notifyDataSetChanged();
    

    3-我重写了mPagerAdapter中的getItemPosition()并使其返回POSITION_NONE

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
        
    除了一种情况外,其他情况下都能工作。当你删除viewPager中的最后一个项目时。那么它就不会从列表中删除。
    我正在动态地将片段添加到viewpager中当前片段的左边和右边。这一次成功了!谢谢。
    oscarthecat
    oscarthecat
    发布于 2020-04-09
    0 人赞同

    我遇到了这个问题,今天终于解决了,所以我把我学到的东西写下来,希望对刚接触安卓的ViewPager的人有帮助,也希望像我一样更新。我在API级别17中使用FragmentStatePagerAdapter,目前只有2个片段。我想一定有什么地方不对,请指正,谢谢。

  • 串行化的数据必须被加载到内存中。这可以用CursorLoader/AsyncTask/Thread来完成。它是否被自动加载取决于你的代码。如果你使用的是CursorLoader,它是自动加载的,因为有一个注册的数据观察器。

  • 在你调用viewpager.setAdapter(pageradapter)后,适配器的getCount()会不断被调用以建立片段。因此,如果正在加载数据,getCount()可以返回0,因此你不需要为没有显示的数据创建虚拟片段。

  • 数据加载后,由于getCount()仍为0,适配器不会自动建立片段,所以我们可以将实际加载的数据号设置为由getCount()返回,然后调用适配器的notifyDataSetChanged()ViewPager开始按内存中的数据创建片段(只是前两个片段)。这是在notifyDataSetChanged()被返回之前完成的。然后ViewPager就有了你需要的正确片段。

  • 如果数据库和内存中的数据都被更新(写通),或者只是内存中的数据被更新(写回),或者只有数据库中的数据被更新。在后两种情况下,如果数据没有自动从数据库加载到内存(如上所述)。 替换代码0】和传呼机适配器只是处理内存中的数据。

  • 所以当内存中的数据被更新时,我们只需要调用适配器的notifyDataSetChanged()。由于片段已经被创建,适配器的onItemPosition()将在notifyDataSetChanged()返回之前被调用。在getItemPosition()中不需要做什么。然后,数据被更新。

  • 我发现在你更新数据(在内存中)后不需要做什么,只要调用notifyDataSetChanged()就会自动更新,因为我使用的片段(带图表视图)会自动刷新。如果不是这样,比如一个静态片段,我将不得不调用onItemPositon()并返回POSITION_NONE,对此我很抱歉。
    有谁知道是用什么工具做的这张图吗?
    czaku
    czaku
    发布于 2020-04-09
    0 人赞同

    在你的代码中,在ViewPager上的destroyDrawingCache()之后尝试notifyDataSetChanged()

    Seth
    我也一样。很好,谢谢你。我有一个特别困难的时间,因为我没有在我的视图页中使用Fragments。所以,谢谢你!
    Sami Eltamawy
    Sami Eltamawy
    发布于 2020-04-09
    0 人赞同

    经过几个小时的挫折,同时尝试所有上述解决方案来克服这个问题,也尝试了许多其他类似问题的解决方案,如this, thisthis which all FAILED with me to solve this problem和to make the ViewPager to destroy the old Fragment和fill the pager with the new Fragments. I have solved the problem as following:

    1)使ViewPager类扩展为FragmentPagerAdapter,如下所示。

     public class myPagerAdapter extends FragmentPagerAdapter {
    

    2) Create an Item for the ViewPager that store the title和the fragment as following:

    public class PagerItem {
    private String mTitle;
    private Fragment mFragment;
    public PagerItem(String mTitle, Fragment mFragment) {
        this.mTitle = mTitle;
        this.mFragment = mFragment;
    public String getTitle() {
        return mTitle;
    public Fragment getFragment() {
        return mFragment;
    public void setTitle(String mTitle) {
        this.mTitle = mTitle;
    public void setFragment(Fragment mFragment) {
        this.mFragment = mFragment;
    

    3)使ViewPager的构造函数取我的FragmentManager实例,并将其存储在我的class中,如下所示。

    private FragmentManager mFragmentManager;
    private ArrayList<PagerItem> mPagerItems;
    public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
        super(fragmentManager);
        mFragmentManager = fragmentManager;
        mPagerItems = pagerItems;
    

    4)创建一个方法来重新通过直接从fragmentManager中删除所有以前的fragment,使adapter从新的列表中重新设置新的fragment,如下所示。

    public void setPagerItems(ArrayList<PagerItem> pagerItems) {
        if (mPagerItems != null)
            for (int i = 0; i < mPagerItems.size(); i++) {
                mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        mPagerItems = pagerItems;
    

    5)从容器ActivityFragment不要用新的数据重新初始化适配器。通过方法setPagerItems设置新数据,新数据如下。

    ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
        pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
        pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));
        mPagerAdapter.setPagerItems(pagerItems);
        mPagerAdapter.notifyDataSetChanged();
    

    I hope it helps.

    你能帮助我解决这个问题吗?stackoverflow.com/questions/40149039/...
    能否请你帮助我解决这个问题?stackoverflow.com/questions/41547995/...
    hordurh
    hordurh
    发布于 2020-04-09
    0 人赞同

    由于某些原因,没有一个答案对我有用,所以我不得不在我的fragmentStatePagerAdapter中覆盖restoreState方法而不调用super。代码。

    private class MyAdapter extends FragmentStatePagerAdapter {
        // [Rest of implementation]
        @Override
        public void restoreState(Parcelable state, ClassLoader loader) {}
        
    在尝试了这个SO答案中的其他所有内容后,这是对我有效的一个答案。我正在完成()一个活动,该活动将返回到承载有关PagerAdapter的活动。我想在这种情况下重新创建我的所有片段,因为我刚刚结束的活动可能已经改变了用于绘制片段的状态。
    OMG. It works...it works... kudos :D 在我的案例中,我需要移除viewpager的当前项目对象。但在尝试了上述所有方法/步骤后,当前片段没有更新。
    restoreState在后来的FragmentStatePagerAdapter版本中被标记为最终版本。
    是的,这个答案是6年前的。从那时起,寻呼机的东西已经改进了。
    adwait
    adwait
    发布于 2020-04-09
    0 人赞同

    I slightly modified the solution provided by Bill Phillips to suit my needs

    private class PagerAdapter extends FragmentStatePagerAdapter{
        Bundle oBundle;
        FragmentManager oFragmentManager;
        ArrayList<Fragment> oPooledFragments;
    public PagerAdapter(FragmentManager fm) {
        super(fm);
        oFragmentManager=fm;
    @Override
    public int getItemPosition(Object object) {
        Fragment oFragment=(Fragment)object;
        oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
        if(oPooledFragments.contains(oFragment))
            return POSITION_NONE;
            return POSITION_UNCHANGED;
    

    这样,当getItemPosition被调用时,getItemPosition()只返回POSITION_NONE中的那些片段,这些片段目前在FragmentManager中。 (注意,这个FragmentStatePager和与之相关的ViewPager是包含在一个片段中,而不是在一个活动中。)

    jpm
    jpm
    发布于 2020-04-09
    0 人赞同

    我有一个类似的问题,但不想相信现有的解决方案(硬编码的标签名称等),我无法使M-WaJeEh的解决方案对我有用。以下是我的解决方案。

    I keep references to the fragments created in getItem in an array. This works fine as long as the activity is not destroyed due to configurationChange or lack of memory or whatever (--> when coming back to the activity, fragments return to their last state without 'getItem' being called again and thus without updating the array).

    为了避免这个问题,我实现了instantiateItem(ViewGroup, int)并在那里更新我的数组,像这样。

            @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Object o = super.instantiateItem(container, position);
            if(o instanceof FragmentX){
                myFragments[0] = (FragmentX)o;
            }else if(o instanceof FragmentY){
                myFragments[1] = (FragmentY)o;
            }else if(o instanceof FragmentZ){
                myFragments[2] = (FragmentZ)o;
            return o;
    

    所以,一方面我很高兴我找到了一个适合我的解决方案,并想与你分享,但我也想问问是否有人尝试过类似的东西,是否有什么理由我不应该这样做?到目前为止,它对我来说非常有效...

    qix
    我曾在以下方面做过相关工作stackoverflow.com/a/23427215/954643尽管你可能应该把myFragments中的项目也从public void destroyItem(ViewGroup container, int position, Object object)中删除,这样你就不会抓到一个过时的fragement引用。
    nebyan
    nebyan
    发布于 2020-04-09
    0 人赞同

    我也遇到了同样的问题,我已经搜索了很多次。在stackoverflow或通过谷歌给出的任何答案都不能解决我的问题。我的问题很简单。我有一个列表,我用viewpager显示这个列表。当我在列表的头部添加一个新元素,并刷新viewpager时,什么都没有改变。我的最终解决方案非常简单,任何人都可以使用。当一个新的元素添加到列表中并想刷新列表时。首先将viewpager适配器设置为null,然后重新创建适配器并将其设置为viewpager。

    myVPager.setAdapter(null);
    myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
    myVPager.setAdapter(myFragmentAdapter);
    

    请确保你的适配器必须扩展FragmentStatePagerAdapter

    Fantasy Fang
    Fantasy Fang
    发布于 2020-04-09
    0 人赞同

    I use EventBus库来更新Fragment中的内容ViewPager。这个逻辑很简单,就像document of EventBus how to do.它不需要控制FragmentPagerAdapter实例。代码在这里。

    1: Define events

    定义需要更新的信息。

    public class UpdateCountEvent {
        public final int count;
        public UpdateCountEvent(int count) {
            this.count = count;
    

    2.准备订阅者

    在需要更新的片段中写下以下代码。

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    @Override
    public void onStop() {
        EventBus.getDefault().unregister(this);  
        super.onStop();
    public void onEvent(UpdateCountEvent event) {//get update message
        Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
    

    3.Post events

    在其他需要更新参数的Activity或其他Fragment中写入以下代码

    //input update message
    EventBus.getDefault().post(new UpdateCountEvent(count));
        
    Ted  Hsieh
    Ted Hsieh
    发布于 2020-04-09
    0 人赞同

    我一直在尝试许多不同的方法,但没有一个能真正解决我的问题。下面是我如何用大家提供的混合解决方案来解决这个问题。谢谢大家。

    class PagerAdapter extends FragmentPagerAdapter {
        public boolean flag_refresh=false;
        public PagerAdapter(FragmentManager fm) {
            super(fm);
        @Override
        public Fragment getItem(int page) {
            FragmentsMain f;
            f=new FragmentsMain();
            f.page=page;
            return f;
        @Override
        public int getCount() {
            return 4;
        @Override
        public int getItemPosition(Object item) {
            int page= ((FragmentsMain)item).page;
            if (page == 0 && flag_refresh) {
                flag_refresh=false;
                return POSITION_NONE;
            } else {
                return super.getItemPosition(item);
        @Override
        public void destroyItem(View container, int position, Object object) {
            ((ViewPager) container).removeView((View) object);
    

    我只想在onResume()之后刷新第0页。

     adapter=new PagerAdapter(getSupportFragmentManager());
     pager.setAdapter(adapter);
    @Override
    protected void onResume() {
        super.onResume();
        if (adapter!=null) {
            adapter.flag_refresh=true;
            adapter.notifyDataSetChanged();
    

    在我的FragmentsMain中,有一个公共的整数 "page",它可以告诉我这是否是我想刷新的页面。

    public class FragmentsMain extends Fragment {
    private Cursor cursor;
    private static Context context;
    public int page=-1;
        
    theapache64
    theapache64
    发布于 2020-04-09
    0 人赞同

    我知道我在聚会上迟到了。我已经通过在TabLayout#setupWithViewPager(myViewPager);之后调用FragmentPagerAdapter#notifyDataSetChanged();来解决这个问题。

    cgr
    cgr
    发布于 2020-04-09
    0 人赞同

    如果你想使用FragmentStatePagerAdapter,请看一下https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=37990. FragmentStatePagerAdapter有一些问题,可能会或可能不会给你的用例带来麻烦。

    另外,链接中也有一些解决方案,一些可能适合你的要求。

    从这个网址我找到了解决方案。我使用了这个gist中修改过的FragmentStatePagerAdaptergist.github.com/ypresto/8c13cb88a0973d071a64在我的代码中,它开始按规定工作。
    cgr
    酷。是的。链接也有一些解决方案。
    Meanman
    Meanman
    发布于 2020-04-09
    0 人赞同

    这可能对某人有帮助--在我的例子中,当插入一个新的页面时,查看器两次询问现有片段的位置,但没有询问新项目的位置,导致行为不正确,数据不显示。

  • 复制FragmentStatePagerAdapter的源代码(似乎已经很久没有更新了)。

  • 覆盖 notifyDataSetChanged()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    
  • 为destroyItem()添加理智检查以防止崩溃。

    if (position < mFragments.size()) {
        mFragments.set(position, null);
        
  • voam
    voam
    发布于 2020-04-09
    0 人赞同

    这是我结合@Bill Phillips的信息实现的。 一个人在大多数时候都能得到片段缓存,除非数据已经改变。很简单,而且看起来运行良好。

    MyFragmentStatePagerAdapter.java

    private boolean mIsUpdating = false;
    public void setIsUpdating(boolean mIsUpdating) {
            this.mIsUpdating = mIsUpdating;
    @Override
    public int getItemPosition(@NonNull Object object) {
        if (mIsUpdating) {
            return POSITION_NONE;
        else {
            return super.getItemPosition(object);
    

    MyActivity.java

    mAdapter.setIsUpdating(true);
    mAdapter.notifyDataSetChanged();
    mAdapter.setIsUpdating(false);
        
    jacoballenwood
    jacoballenwood
    发布于 2020-04-09
    0 人赞同

    Using ViewPager2 and FragmentStateAdapter:

    动态更新数据是由ViewPager2支持的。

    其中有一个重要的说明docs on how to get this working:

    注意:DiffUtil实用类依赖于通过ID来识别项目。如果你使用ViewPager2来翻阅一个可变的集合,你也必须覆盖getItemId()containsItem()(强调是我的)

    Based on ViewPager2文件和安卓的Github样品项目有几个步骤我们需要采取。

  • Set up FragmentStateAdapter and override the following methods: getItemCount, createFragment, getItemId, and containsItem (note: FragmentStatePagerAdapter is not supported by ViewPager2)

  • 将适配器安装在ViewPager2

  • Dispatch list updates to ViewPager2 with DiffUtil (don't need to use DiffUtil, as seen in 样品项目)

    private val items: List<Int>
        get() = viewModel.items
    private val viewPager: ViewPager2 = binding.viewPager
    private val adapter = object : FragmentStateAdapter(this@Fragment) {
        override fun getItemCount() = items.size
        override fun createFragment(position: Int): Fragment = MyFragment()
        override fun getItemId(position: Int): Long = items[position].id
        override fun containsItem(itemId: Long): Boolean = items.any { it.id == itemId }
    viewPager.adapter = adapter
    private fun onDataChanged() {
        DiffUtil
            .calculateDiff(object : DiffUtil.Callback() {
                override fun getOldListSize(): Int = viewPager.adapter.itemCount
                override fun getNewListSize(): Int = viewModel.items.size
                override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                    viewPager.adapter?.getItemId(oldItemPosition) == viewModel.items[newItemPosition].id
                override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                    areItemsTheSame(oldItemPosition, newItemPosition)
            }, false)
            .dispatchUpdatesTo(viewPager.adapter!!)
        
  • 问题是在Java中,你为什么要把解决方案放在Kotlin中?
    emen
    @Teocci很好,对于使用最新软件包的人来说,很容易解决这个问题。
    Kevin Grant
    Kevin Grant
    发布于 2020-04-09
    0 人赞同

    这个解决方案并不适合每个人,但在我的例子中,我的ViewPager中的每个Fragment都是一个不同的类,而且每次只有一个存在。

    有了这个约束条件,这个解决方案是安全的,在生产中使用应该是安全的。

    private void updateFragment(Item item) {
        List<Fragment> fragments = getSupportFragmentManager().getFragments();
        for (Fragment fragment : fragments) {
            if (fragment instanceof MyItemFragment && fragment.isVisible()) {
                ((MyItemFragment) fragment).update(item);
    

    如果你有同一片段的多个版本,你可以使用这个相同的策略来调用这些片段的方法,以确定它是否是你希望更新的片段。

    John O'Reilly
    John O'Reilly
    发布于 2020-04-09
    0 人赞同

    我看了上面所有的答案和其他一些帖子,但还是找不到对我有用的东西(不同的片段类型和动态添加和删除标签)。 下面的方法对我来说是有效的(如果其他人也有同样的问题)。

    public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
        private static final String TAB1_TITLE = "Tab 1";
        private static final String TAB2_TITLE = "Tab 2";
        private static final String TAB3_TITLE = "Tab 3";
        private ArrayList<String> titles = new ArrayList<>();
        private Map<Fragment, Integer> fragmentPositions = new HashMap<>();
        public MyFragmentStatePageAdapter(FragmentManager fm) {
            super(fm);
        public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
            titles.clear();
            if (showTab1) {
                titles.add(TAB1_TITLE);
            if (showTab2) {
                titles.add(TAB2_TITLE);
            if (showTab3) {
                titles.add(TAB3_TITLE);
            notifyDataSetChanged();
        @Override
        public int getCount() {
            return titles.size();
        @Override
        public Fragment getItem(int position) {
            Fragment fragment = null;
            String tabName = titles.get(position);
            if (tabName.equals(TAB1_TITLE)) { 
                fragment =  Tab1Fragment.newInstance();
            } else if (tabName.equals(TAB2_TITLE)) {
                fragment =  Tab2Fragment.newInstance();
            } else if (tabName.equals(TAB3_TITLE)) {
                fragment =  Tab3Fragmen.newInstance();
            ((BaseFragment)fragment).setTitle(tabName);
            fragmentPositions.put(fragment, position);
            return fragment;
        @Override
        public CharSequence getPageTitle(int position) {
            return titles.get(position);
        @Override
        public int getItemPosition(Object item) {
            BaseFragment fragment = (BaseFragment)item;
            String title = fragment.getTitle();
            int position = titles.indexOf(title);
            Integer fragmentPosition = fragmentPositions.get(item);
            if (fragmentPosition != null && position == fragmentPosition) {
                return POSITION_UNCHANGED;
            } else {
                return POSITION_NONE;
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            super.destroyItem(container, position, object);
            fragmentPositions.remove(object);
        
    HemangNirmal
    HemangNirmal
    发布于 2020-04-09
    0 人赞同

    使用 FragmentStatePagerAdapter 而不是 FragmentPagerAdapter 如果你想在索引的基础上重新创建或重新加载片段。 例如,如果你想重新加载FirstFragment以外的片段,你可以像这样检查实例和返回位置

     public int getItemPosition(Object item) {
        if(item instanceof FirstFragment){
           return 0;
        return POSITION_NONE;
        
    changhao cui
    changhao cui
    发布于 2020-04-09
    0 人赞同

    你需要改变instantiateItem的mFragments元素getItemPosition。

        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                int newPosition = getItemPosition(f);
                if (newPosition == POSITION_UNCHANGED) {
                    return f;
                } else if (newPosition == POSITION_NONE) {
                    mFragments.set(position, null);
                } else {
                    mFragments.set(newPosition, f);
    

    基于AndroidX FragmentStatePagerAdapter.java,因为mFragments的元素位置在调用notifyDataSetChanged()时没有变化。

    Source: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java