dynamic更新ViewPager?

我无法更新ViewPager中的内容。

一个问题:在FragmentPagerAdapter类中,方法instantiateItem()和getItem()的关系和正确用法是什么?

我只使用getItem()来实例化并返回我的片段:

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

这工作得很好(但据说我不能改变内容)。

所以我发现: ViewPager PagerAdapter没有更新视图

特别是在其中谈到方法instantiateItem():

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

所以现在我想实现instantiateItem()为了做到这一点。 但我不知道我必须在那里返回(返回是Object)和getItem(int位置)的关系是什么?

目前我使用getItem来实例化片段,是不是? 但是,那么我必须把碎片放在实例variables中,或者根本不实现getItem()…? 我只是不明白这一点。

我尝试阅读参考 :

  • public abstract Fragment getItem(int position)

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

  • 公共对象instantiateItem(ViewGroup容器,int位置)

    创build给定位置的页面。 适配器负责将视图添加到这里给出的容器,尽pipe它只能确保在从finishUpdate(ViewGroup)返回时完成。 参数

    容器包含将在其中显示页面的View。 position要实例化的页面位置。

    返回

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

…但我仍然不明白他们是怎么相关的,我该怎么做。

这是我的代码。 我正在使用支持包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; } } 

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

当使用FragmentPagerAdapter或FragmentStatePagerAdapter时,最好只处理getItem()而不要触碰instantiateItem()isViewFromObject()instantiateItem()destroyItem()isViewFromObject()接口是FragmentPagerAdapter用来实现更简单的getItem()接口的低级接口。

在进入这个之前,我应该澄清一下

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

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

我不build议您链接的post中提供的setTag()findViewWithTag()解决方法。 正如你已经发现,使用setTag()findViewWithTag()不能使用片段,所以它不是一个很好的匹配。

正确的解决scheme是重写getItemPosition() 。 当调用getItemPosition()时,ViewPager会调用其适配器中所有项目的getItemPosition() ,以查看它们是否需要移动到不同位置或移除。

默认情况下, getItemPosition()返回POSITION_UNCHANGED ,这意味着“这个对象没有问题,不要销毁或删除它”。 返回POSITION_NONE通过改为“这个对象不再是我正在显示的项目,将其删除”来解决问题。 所以它具有删除和重新创build适配器中的每个项目的效果。

这是一个完全合法的修复! 此修复程序使notifyDataSetChanged的行为与普通适配器无关,无需查看回收。 如果你执行这个修补程序,并且性能令人满意,那么你就可以参加比赛了。 任务完成。

如果你需要更好的性能,你可以使用一个更好的getItemPosition()实现。 下面是一个分页器创build一个string列表的例子:

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

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

如果片段没有被重新创build,但是需要更新呢? 对活动片段的更新最好由片段本身来处理。 毕竟,这是有一个片段的优势 – 它是它自己的控制器。 片段可以将一个监听器或一个观察者添加到onCreate()另一个对象,然后在onDestroy()中将其删除,从而自行pipe理更新。 您不必像在ListView或其他AdapterViewtypes的适配器中那样将所有更新代码放在getItem()

最后一件事 – 只是因为FragmentPagerAdapter不销毁一个片段并不意味着getItemPosition在FragmentPagerAdapter中是完全无用的。 您仍然可以使用此callback在ViewPager中对您的片段进行重新sorting。 但是它永远不会从FragmentManager中完全删除它们。

而不是从getItemPosition()返回POSITION_NONE并导致完整的视图重新创build,请执行以下操作:

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

我遇到了这个问题,今天终于解决了,所以我写下了我所学到的内容,希望对于Android的ViewPager帮助。 我在API级别17中使用FragmentStatePagerAdapter ,目前只有2个片段。 我认为一定是不对的,请纠正我,谢谢。 在这里输入图像描述

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

  2. 在调用viewpager.setAdapter(pageradapter) ,不断调用适配器的getCount()来构build片段。 所以,如果数据被加载, getCount()可以返回0,因此不需要为显示的数据创build虚拟片段。

  3. 数据加载后,由于getCount()仍为0,适配器不会自动构build分片,所以我们可以设置getCount()返回的实际加载的数据编号,然后调用适配器的notifyDataSetChanged()ViewPager开始通过内存中的数据创build片段(只是前两个片段)。 它在notifyDataSetChanged()返回之前完成。 然后, ViewPager有你需要的正确的片段。

  4. 如果数据库和内存中的数据都更新(通过写入),或者只更新内存中的数据(写回),或者只更新数据库中的数据。 在最后两种情况下,如果数据不是从数据库自动加载到内存(如上所述)。 ViewPager和寻呼机适配器只处理内存中的数据。

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

在代码中的notifyDataSetChanged()之后,尝试在ViewPager上使用notifyDataSetChanged()

对于那些仍然面临同样的问题,我面前有七个片段的观点。 这些片段的默认加载从服务的英文内容,但问题在这里,我想从设置活动,并在完成后设置活动我希望viewpager在主要活动刷新片段以匹配从用户的语言select和加载阿拉伯文内容,如果用户在这里select阿拉伯文我第一次做什么工作

1 – 您必须使用上面提到的vFragmentStatePagerAdapter。

2 – 在mainActivity我override的onResume做了以下

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

3 -i在mPagerAdapter中使用getItemPosition(),并使其返回POSITION_NONE。

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

像魅力一样工作

经过几个小时的挫折,同时尝试所有上述解决scheme来克服这个问题,也尝试了很多解决scheme,像这样的其他类似的问题, 这和这一切都失败了,我解决了这个问题,并使ViewPager破坏旧的Fragment和填充带有新Fragmentpager 。 我已经解决了这个问题,如下所示:

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

  public class myPagerAdapter extends FragmentPagerAdapter { 

2)为存储titlefragmentViewPager创build一个Item,如下所示:

 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的构造ViewPager把我的FragmentManager实例存储在我的class ,如下所示:

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

4)创build一个方法,用新的数据重新设置adapter数据,直接删除fragment所有前一个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方法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(); 

我希望它有帮助。

由于某种原因,没有答案为我工作,所以我不得不重写restoreState方法,而不是在我的fragmentStatePagerAdapter调用超级。 码:

 private class MyAdapter extends FragmentStatePagerAdapter { // [Rest of implementation] @Override public void restoreState(Parcelable state, ClassLoader loader) {} } 

我稍微修改了Bill Phillips提供的解决scheme,以满足我的需求

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

所以getItemPosition()仅在getItemPosition()返回当前位于FragmentManager中的FragmentManager getItemPosition 。 (请注意,这个FragmentStatePagerViewPager关联的ViewPager包含在一个不在Activity中的Fragment中)

我有类似的问题,但不想相信现有的解决scheme(硬编码标签名称等),我不能让M-WaJeEh的解决scheme为我工作。 这是我的解决scheme:

我保持对数组中创build的片段的引用。 这个工作正常,只要活动没有因为configurationChange或内存不足而被破坏( – >当回到活动时,fragment会返回到最后一个状态,而不会再次调用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; } 

所以,一方面我很高兴我find了一个适合我的解决scheme,想和大家分享一下,但是我也想问问别人是否也尝试了类似的东西,我是否有理由不这样做这样做吗? 到目前为止,它对我来说非常好…

如果您想使用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的问题,可能会或可能不会麻烦您的使用情况。

此外,链接也有一些解决scheme太..可能会适合您的要求。

我使用EventBus库来更新ViewPager Fragment内容。 逻辑很简单,就像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)); 

我一直在尝试这么多种不同的方法,没有一个能真正解决我的问题。 以下是我如何解决你所有提供的解决scheme的混合。 感谢大家。

 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; 

我知道党迟到了。 我已经通过调用TabLayout#setupWithViewPager(myViewPager);解决了这个问题TabLayout#setupWithViewPager(myViewPager); 就在FragmentPagerAdapter#notifyDataSetChanged();

这个解决scheme不适用于所有人,但在我的情况下,ViewPager中的每个片段都是不同的类,每次只有其中一个存在。

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

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

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

这可能对某人有所帮助 – 就我而言,当插入一个新页面的时候,视图寻呼机要求现有片段的位置两次,而不是要求新项目的位置,导致不正确的行为和数据不显示。

  1. 复制FragmentStatePagerAdapter的源代码(似乎还没有更新过)。

  2. 覆盖notifyDataSetChanged()

     @Override public void notifyDataSetChanged() { mFragments.clear(); super.notifyDataSetChanged(); } 
  3. 添加一个完整性检查destroyItem(),以防止崩溃:

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

接受的答案是不明白的,这就是为什么添加简单的解决scheme。 find工作解决scheme

只要通知你的适配器 ,它的工作令人惊叹,

参考代码

  void setAdapter(int position) { PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(pagerAdapter); // when notify then set manually current position. viewPager.setCurrentItem(position); pagerAdapter.notifyDataSetChanged(); } 

当您设置适配器并且想要刷新UI时调用此代码。

在您的片段中添加此代码进行刷新 使用instanceof进行投射上下文。

 ((YourActivityName) getContext()).setAdapter(selectedTabPosition); 

此代码共享只是帮助寻找工作解决scheme的目的,希望它有帮助!

我已经通过了上面的所有答案和其他一些post,但仍然找不到对我有用的东西(使用不同的片段types以及dynamic添加和删除标签)。 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); } } 

我生活的同样的问题,我已经搜查了太多次。 任何在stackoverflow或通过谷歌给出的答案不解决我的问题。 我的问题很简单。 我有一个列表,我用viewpager显示这个列表。 当我添加一个新元素到列表的头部,并刷新viewpager nothings改变。 我的最终解决scheme非常简单,任何人都可以使用。 当一个新的元素添加到列表,并希望刷新列表。 首先将viewpager适配器设置为null,然后重新创build适配器,并将其设置为viewpager。

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

确保您的适配器必须扩展FragmentStatePagerAdapter