动态更新ViewPager?


316

我无法更新ViewPager中的内容。

FragmentPagerAdapter类中的InstantIateItem()和getItem()方法的正确用法是什么?

我仅使用getItem()实例化并返回片段:

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

这很好。除了我不能更改内容。

所以我发现了这一点:ViewPager PagerAdapter不更新视图

“我的方法是将setTag()方法用于instantiateItem()方法中的任何实例化视图”

现在,我想实现实例化item()。但是我不知道我必须返回什么(类型为Object)以及与getItem(int position)的关系是什么?

我阅读了参考资料

  • 公共抽象片段getItem(int位置)

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

  • public Object InstantiateItem(ViewGroup容器,int位置)

    创建给定位置的页面。适配器负责将视图添加到此处给定的容器中,尽管它仅必须确保在从finishUpdate(ViewGroup)返回时完成此操作。参量

    container包含页面的显示视图。position要实例化的页面位置。

    退货

    返回表示新页面的Object。这不必是View,但可以是页面的其他容器。

但我还是不明白。

这是我的代码。我正在使用支持包v4。

ViewPagerTest

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;
    }
}

我的碎片

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


为了更好地理解所涉及的组件,可能有助于阅读有关带单独后退导航历史的选项卡式ViewPager的教程。它带有GitHub上的工作示例以及其他资源:medium.com/@nilan/…–
marktani


您可以在GitHub中查看此示例。希望这个例子对您有所帮助。
Cabezas

好的简单解决方案:stackoverflow.com/a/35450575/2162226
Gene Bo,

Google最近推出了ViewPager2,它简化了动态Fragment / View更新。您可以在github.com/googlesamples/android-viewpager2查看示例,也可以在stackoverflow.com/a/57393414/1333448
Yves Delerm,

Answers:


605

当使用FragmentPagerAdapter或FragmentStatePagerAdapter时,最好单独处理getItem()而不要碰触instantiateItem()。本instantiateItem()- destroyItem()- isViewFromObject()在PagerAdapter接口是较低级别的接口,FragmentPagerAdapter用来实现更简单的getItem()界面。

在讨论之前,我应该澄清一下

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

此答案的较早版本在示例中使用FragmentPagerAdapter犯了一个错误-无法使用,因为FragmentPagerAdapter从未在第一次显示该片段后销毁该片段。

我不建议您链接的文章中提供setTag()findViewWithTag()解决方法。正如您所发现的,使用setTag()findViewWithTag()不能与片段一起使用,因此这不是一个很好的选择。

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

默认情况下,getItemPosition()返回POSITION_UNCHANGED,表示“该对象在任何位置都可以,请不要破坏或删除它”。返回POSITION_NONE可解决问题,而改为说:“此对象不再是我要显示的项目,请将其删除。” 因此,它具有删除并重新创建适配器中每个项目的效果。

这是完全合法的修复!此修复程序使notifyDataSetChanged的行为类似于常规的适配器,而没有视图回收。如果实施此修复程序并且性能令人满意,那么您就可以参加比赛了。任务完成。

如果需要更好的性能,可以使用更高级的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(),从而管理更新本身。您不必getItem()像在ListView或其他AdapterView类型的适配器中那样放入所有更新代码。

最后一件事-仅仅因为FragmentPagerAdapter不会破坏一个片段,并不意味着getItemPosition在FragmentPagerAdapter中是完全无用的。您仍然可以使用此回调在ViewPager中重新排序片段。但是,它将永远不会从FragmentManager中完全删除它们。


4
不起作用,它仍然没有更新任何内容...发布了我的代码。
Ixx 2012年

64
好的,解决了这个问题-您需要使用FragmentStatePagerAdapter而不是FragmentPagerAdapter。FragmentPagerAdapter永远不会从FragmentManager中删除片段,它只会分离并附加它们。检查文档以获取更多信息-通常,您只想将FragmentPagerAdapter用于永久性的片段。我用更正对示例进行了编辑。
Bill Phillips

1
稍后我会对此进行审查...感谢您的回答。然后,我接受您的回答(如果可行)。在您发表最后一条评论之前,我实现了每次都删除并添加一个新的ViewPager的方法,并且可以正常工作。解决方案不是很好,但是我现在没有时间进行更改。
Ixx 2012年

12
没错,将适配器的类更改为FragmentStatePagerAdapter可以解决所有麻烦。谢谢!
Ixx 2012年

2
即使在使用时返回POSITION_NONE,FragmentStatePagerAdapter我也可以看到它删除了我的片段并创建了新实例。但是新实例会接收到带有旧片段状态的saveInstanceState Bundle。如何完全销毁片段,以便再次创建时Bundle为null?
2016年

142

与其POSITION_NONE从中返回getItemPosition()并引起全视图娱乐,请执行以下操作:

//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
}

1
这是否意味着您的MainActivity也需要实现接口?请帮我在这里。在当前的实现中,我将MainActivity类用作片段之间的通信器,因此我对该解决方案感到困惑。
Rakeeb Rajbhandari

1
好吧,我已经在全实现,其中我更新另一个地方回答Fragment动态,看看:stackoverflow.com/questions/19891828/...
M-WaJeEh

@ M-WaJeEh也许请举个例子!!!我有一个带有城市列表的列表视图,选择城市后,打开包含有关该城市信息的viewpager(3页)。可以。但是在选择其他城市的浏览器后,只会显示d page on refreshes the 1-st page only after swiping pages? thw 23d页面始终为空白。谢谢
SERG

4
在我过去的2.5天工作中见过的所有东西中,..这是唯一为我工作的东西..各种各样的arigato,谢谢,spaciba,sukran .. n。
Orkun Ozen

2
龙之母!,这就像一种魅力,对我来说唯一有效的解决方案……非常感谢!
塞巴斯蒂安·格雷罗2015年

24

对于那些仍然面临我之前遇到的同样问题的人,当我有ViewPager7个片段时。这些片段的默认设置是从API服务中加载英语内容,但是这里的问题是我想从设置活动中更改语言,完成设置活动后我想要ViewPager在主活动中刷新片段以匹配用户的语言选择并加载如果用户在这里选择阿拉伯语,则是阿拉伯语内容

1-您必须使用如上所述的FragmentStatePagerAdapter。

2- on mainActivity我覆盖onResume并执行以下操作

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

3-i覆盖了getItemPosition()mPagerAdapter中的,并使其返回POSITION_NONE

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
}

像魅力一样工作


1
一项工作除外。当您删除viewPager中的最后一项时。然后它不会从列表中删除。
弗拉多·潘迪奇(VladoPandžić)2016年

我正在动态地在viewpager中将片段添加到当前片段的左侧和右侧。这个工作了!谢谢。
h8pathak '18

15

我遇到了这个问题,今天终于解决了,所以我记下我所学到的东西,希望它对新接触Android ViewPager并像我一样进行更新的人有所帮助。我FragmentStatePagerAdapter在API级别17中使用,目前只有2个片段。我认为一定有不正确的地方,请纠正我,谢谢。 在此处输入图片说明

  1. 序列化的数据必须加载到内存中。可以使用CursorLoader/ AsyncTask/完成Thread。是否自动加载取决于您的代码。如果您使用CursorLoader,则会自动加载,因为有一个已注册的数据观察器。

  2. 调用之后viewpager.setAdapter(pageradapter),将getCount()不断调用适配器以构建片段。因此,如果正在加载数据,则getCount()可以返回0,因此无需为没有显示数据而创建虚拟片段。

  3. 加载数据后,适配器getCount()仍为0,因此不会自动构建片段,因此我们可以设置实际加载的数据号,以返回getCount(),然后调用适配器的notifyDataSetChanged()ViewPager开始通过内存中的数据创建片段(仅前两个片段)。在notifyDataSetChanged()返回之前已完成。然后,您便ViewPager拥有了所需的正确片段。

  4. 如果数据库和内存中的数据都被更新(直写),或者仅内存中的数据被更新(写回),或者仅数据库中的数据被更新。在后两种情况下,如果数据没有自动从数据库加载到内存中(如上所述)。在ViewPager和寻呼机转接器只是处理内存中的数据。

  5. 因此,当内存中的数据更新时,我们只需要调用适配器的即可notifyDataSetChanged()。由于片段已经创建,因此适配器onItemPosition()将在notifyDataSetChanged()返回之前被调用。不需要做任何事情getItemPosition()。然后更新数据。


我发现更新数据(在内存中)后不需要执行任何操作,只需调用notifyDataSetChanged()然后自动更新,因为我使用的片段(带有chartview)会刷新自身。如果不是,就像一个静态片段一样,我将不得不打电话给onItemPositon()它返回POSITION_NONE 抱歉
oscarthecat

有谁知道使用哪种工具制作该图?
JuiCe '16

12

destroyDrawingCache()输入notifyDataSetChanged()代码后,请尝试使用ViewPager 。


3
同样在这里。很好,谢谢。我的日子特别艰难,因为我没有Fragments在视图寻呼机中使用。所以谢谢!
赛斯2015年

11

经过数小时的挫折之后,尝试上述所有解决方案来解决此问题,并尝试解决其他类似问题的解决方案,例如thisthisthis,这一切都失败了,我解决了这个问题,并ViewPager摧毁了旧的Fragmentpager用新Fragment的。我已经解决了以下问题:

1)使ViewPager类扩展FragmentPagerAdapter如下:

 public class myPagerAdapter extends FragmentPagerAdapter {

2)为以下项创建一个用于ViewPager存储title和的项目fragment

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)创建的方法重新设置adapter删除所有以前用新的数据数据fragmentfragmentManager自身直接使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();

希望对您有所帮助。


你能帮我解决我的问题stackoverflow.com/questions/40149039/…–
艾伯特

您能帮我解决这个问题吗stackoverflow.com/questions/41547995/…–
viper

10

由于某种原因,没有一个答案对我有用,因此我不得不重写restoreState方法,而无需在fragmentStatePagerAdapter中调用super。码:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}

1
在尝试了这个SO答案中的所有其他内容之后,这就是为我工作的那个。我正在完成一个活动,该活动将返回到托管有问题的PagerAdapter的活动。在这种情况下,我希望重新创建所有片段,因为刚完成的“活动”可能已更改了用于绘制片段的状态。
JohnnyLambada '16

OMG ..它工作..它工作..荣誉:D就我而言,我需要删除viewpager当前项目对象。但尝试上述所有方法/步骤后,当前片段未更新。
拉胡尔·库拉纳

3

我对Bill Phillips提供的解决方案进行了略微修改,以适应我的需求

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;
    else
        return POSITION_UNCHANGED;
    } 
}

因此,仅针对当前位于when 中的那些片段getItemPosition()返回。(请注意,此代码及其关联的代码包含在Fragment中而不是Activity中)POSITION_NONEFragmentManagergetItemPositionFragmentStatePagerViewPager


2

我遇到了类似的问题,但是不想信任现有的解决方案(硬编码标签名称等),也无法使M-WaJeEh的解决方案对我有用。这是我的解决方案:

我将对在getItem中创建的片段的引用保留在数组中。只要该活动没有由于configurationChange或内存不足或其他原因而被破坏(->返回活动,则片段返回其最后状态而不再次调用'getItem'且因此不更新数组),此方法就可以正常工作)。

为了避免这个问题,我实现了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;
    }

因此,一方面,我很高兴能找到一个对我有用的解决方案,并希望与您分享,但我也想问一问是否有人尝试过类似的东西,以及是否有任何理由我不应该这样做?这样吗?到目前为止,它对我非常有用...


我在有关做了stackoverflow.com/a/23427215/954643,虽然你也许应该从删除的项目myFragmentspublic void destroyItem(ViewGroup container, int position, Object object),以及让你不抢陈旧fragement参考。
2014年

1

我使用EventBus库更新中的Fragment内容ViewPager。逻辑很简单,就像EventBus的文件怎么做。无需控制FragmentPagerAdapter实例。代码在这里:

1:定义事件

定义需要更新的消息。

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.发布事件

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

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));

1

如果要使用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存在一些问题,可能会或可能不会困扰您的用例。

另外,链接也几乎没有解决方案。.可能很少满足您的要求。


通过该URL,我找到了解决方案。我在我的代码中使用了根据要点gist.github.com/ypresto/8c13cb88a0973d071a64修改后的FragmentStatePagerAdapter ,它开始按预期的方式工作。
拉斐尔

凉。是。链接也有很少的解决方案。
cgr

1

我曾遇到过同样的问题,并且搜索了太多次。在stackoverflow或通过google提供的任何答案都不是我的问题的解决方案。我的问题很简单。我有一个列表,并使用viewpager显示此列表。当我将新元素添加到列表的开头并刷新Viewpager时,没有任何改变。我的最终解决方案是任何人都可以轻松使用。当新元素添加到列表中并想要刷新列表时。首先将viewpager适配器设置为null,然后重新创建适配器并将其设置为viewpager。

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

确保适配器必须扩展FragmentStatePagerAdapter


1

这是我的实现,其中包含来自@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);

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中,有一个公共整数“页面”,它可以告诉我它是否是我要刷新的页面。

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;

0

我知道该党迟到了。我已经通过调用固定的问题TabLayout#setupWithViewPager(myViewPager);只是后FragmentPagerAdapter#notifyDataSetChanged();


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

    }
}

如果同一片段有多个版本,则可以使用相同的策略在这些片段上调用方法,以确定它是否是您要更新的片段。


0

这可能对某人有帮助-在我的情况下,插入新页面时,视图分页器两次询问现有片段的位置,但不询问新项目的位置,从而导致错误的行为和数据无法显示。

  1. 复制FragmentStatePagerAdapter的源(似乎年龄尚未更新)。

  2. 覆盖notifyDataSetChanged()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. 向destroyItem()添加健全性检查以防止崩溃:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }

0

我已经遍历了以上所有答案以及许多其他帖子,但是仍然找不到适合我的东西(具有不同的片段类型以及动态添加和删除选项卡)。FWIW遵循的方法对我有效(以防其他人遇到相同的问题)。

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

0

如果要基于索引重新创建或重新加载片段,请使用FragmentStatePagerAdapter而不是FragmentPagerAdapter。例如,如果要重新加载除FirstFragment以外的片段,则可以检查实例并返回位置

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }

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()时的元素位置不会更改。

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

示例:https//github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

您可以运行此项目以确认工作方式。 https://github.com/cuichanghao/infivt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.