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个片段。 我认为一定是不对的,请纠正我,谢谢。
-
序列化的数据必须被加载到内存中。 这可以使用
CursorLoader
/AsyncTask
/Thread
。 是否自动加载取决于你的代码。 如果您使用的是CursorLoader
,则会自动加载,因为有一个注册数据观察器。 -
在调用
viewpager.setAdapter(pageradapter)
,不断调用适配器的getCount()
来构build片段。 所以,如果数据被加载,getCount()
可以返回0,因此不需要为显示的数据创build虚拟片段。 -
数据加载后,由于
getCount()
仍为0,适配器不会自动构build分片,所以我们可以设置getCount()
返回的实际加载的数据编号,然后调用适配器的notifyDataSetChanged()
。ViewPager
开始通过内存中的数据创build片段(只是前两个片段)。 它在notifyDataSetChanged()
返回之前完成。 然后,ViewPager
有你需要的正确的片段。 -
如果数据库和内存中的数据都更新(通过写入),或者只更新内存中的数据(写回),或者只更新数据库中的数据。 在最后两种情况下,如果数据不是从数据库自动加载到内存(如上所述)。
ViewPager
和寻呼机适配器只处理内存中的数据。 -
所以当内存中的数据更新时,我们只需要调用适配器的
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
和填充带有新Fragment
的pager
。 我已经解决了这个问题,如下所示:
1)使ViewPager
类扩展FragmentPagerAdapter
,如下所示:
public class myPagerAdapter extends FragmentPagerAdapter {
2)为存储title
和fragment
的ViewPager
创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)从容器Activity
或Fragment
不要用新数据重新初始化适配器。 使用新数据通过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
。 (请注意,这个FragmentStatePager
和ViewPager
关联的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); } } }
如果您有多个版本的相同片段,则可以使用相同的策略来调用这些片段上的方法,以确定它是否是您希望更新的片段。
这可能对某人有所帮助 – 就我而言,当插入一个新页面的时候,视图寻呼机要求现有片段的位置两次,而不是要求新项目的位置,导致不正确的行为和数据不显示。
-
复制FragmentStatePagerAdapter的源代码(似乎还没有更新过)。
-
覆盖notifyDataSetChanged()
@Override public void notifyDataSetChanged() { mFragments.clear(); super.notifyDataSetChanged(); }
-
添加一个完整性检查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