@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
21 个回答
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中完全删除它们。
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
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;
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()
中不需要做什么。然后,数据被更新。
0 人赞同
在你的代码中,在ViewPager上的destroyDrawingCache()
之后尝试notifyDataSetChanged()
。
0 人赞同
经过几个小时的挫折,同时尝试所有上述解决方案来克服这个问题,也尝试了许多其他类似问题的解决方案,如this, this和this which all FAILED with me to solve this problem和to make the ViewPager
to destroy the old Fragment
和fill the pager
with the new Fragment
s. 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)从容器Activity
或Fragment
不要用新的数据重新初始化适配器。通过方法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.
0 人赞同
由于某些原因,没有一个答案对我有用,所以我不得不在我的fragmentStatePagerAdapter中覆盖restoreState方法而不调用super。代码。
private class MyAdapter extends FragmentStatePagerAdapter {
// [Rest of implementation]
@Override
public void restoreState(Parcelable state, ClassLoader loader) {}
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
是包含在一个片段中,而不是在一个活动中。)
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;
所以,一方面我很高兴我找到了一个适合我的解决方案,并想与你分享,但我也想问问是否有人尝试过类似的东西,是否有什么理由我不应该这样做?到目前为止,它对我来说非常有效...
0 人赞同
我也遇到了同样的问题,我已经搜索了很多次。在stackoverflow或通过谷歌给出的任何答案都不能解决我的问题。我的问题很简单。我有一个列表,我用viewpager显示这个列表。当我在列表的头部添加一个新元素,并刷新viewpager时,什么都没有改变。我的最终解决方案非常简单,任何人都可以使用。当一个新的元素添加到列表中并想刷新列表时。首先将viewpager适配器设置为null,然后重新创建适配器并将其设置为viewpager。
myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);
请确保你的适配器必须扩展FragmentStatePagerAdapter
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));
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;
0 人赞同
我知道我在聚会上迟到了。我已经通过在TabLayout#setupWithViewPager(myViewPager);
之后调用FragmentPagerAdapter#notifyDataSetChanged();
来解决这个问题。
0 人赞同
这可能对某人有帮助--在我的例子中,当插入一个新的页面时,查看器两次询问现有片段的位置,但没有询问新项目的位置,导致行为不正确,数据不显示。
复制FragmentStatePagerAdapter的源代码(似乎已经很久没有更新了)。
覆盖 notifyDataSetChanged()
@Override
public void notifyDataSetChanged() {
mFragments.clear();
super.notifyDataSetChanged();
为destroyItem()添加理智检查以防止崩溃。
if (position < mFragments.size()) {
mFragments.set(position, null);
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);
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!!)
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 人赞同
我看了上面所有的答案和其他一些帖子,但还是找不到对我有用的东西(不同的片段类型和动态添加和删除标签)。 下面的方法对我来说是有效的(如果其他人也有同样的问题)。
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()时没有变化。
Source:
https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java