ViewPager PagerAdapter not updating the View
我正在使用兼容性库中的viewpager。我成功地让它显示了几个我可以翻阅的视图。
但是,我很难弄清楚如何用一组新视图更新viewpager。
每次我想要使用一个新的数据列表时,我都会尝试各种方法,比如调用
没有任何帮助,文本视图与原始数据保持不变。
更新:我做了一个小小的测试项目,几乎可以更新视图。我将把这门课贴在下面。
但是,没有更新的是第二个视图,"B"仍然存在,按下更新按钮后应显示"Y"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | public class ViewPagerBugActivity extends Activity { private ViewPager myViewPager; private List<String> data; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); data = new ArrayList<String>(); data.add("A"); data.add("B"); data.add("C"); myViewPager = (ViewPager) findViewById(R.id.my_view_pager); myViewPager.setAdapter(new MyViewPagerAdapter(this, data)); Button updateButton = (Button) findViewById(R.id.update_button); updateButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { updateViewPager(); } }); } private void updateViewPager() { data.clear(); data.add("X"); data.add("Y"); data.add("Z"); myViewPager.getAdapter().notifyDataSetChanged(); } private class MyViewPagerAdapter extends PagerAdapter { private List<String> data; private Context ctx; public MyViewPagerAdapter(Context ctx, List<String> data) { this.ctx = ctx; this.data = data; } @Override public int getCount() { return data.size(); } @Override public Object instantiateItem(View collection, int position) { TextView view = new TextView(ctx); view.setText(data.get(position)); ((ViewPager)collection).addView(view); return view; } @Override public void destroyItem(View collection, int position, Object view) { ((ViewPager) collection).removeView((View) view); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Parcelable saveState() { return null; } @Override public void restoreState(Parcelable arg0, ClassLoader arg1) { } @Override public void startUpdate(View arg0) { } @Override public void finishUpdate(View arg0) { } } } |
实现这一点有几种方法。
第一种选择更容易,但效率更低。
在您的
1 2 3 | public int getItemPosition(Object object) { return POSITION_NONE; } |
这样,当您调用
第二种选择,由Alvaro Luis Bustamante(以前叫Alvarolb)提出,是在实例化新视图时,使用
第二种方法是非常灵活和高性能的。对阿尔瓦罗布最初研究的赞誉。
我认为
在过去的几天里,我一直在和
我认为,在
这样,如果只想更新页面内容,则重写
因此,有几种方法可以有效地更改视图的内容,而不必再次删除和实例化视图。这取决于你想解决的问题。我的方法是对
例如,假设您有100个带有100个
将
重写
最终,它将收听
阿尔瓦罗布给出的答案无疑是最好的方法。基于他的答案,实现这一点的一个简单方法是简单地按位置存储活动视图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | SparseArray<View> views = new SparseArray<View>(); @Override public Object instantiateItem(View container, int position) { View root = <build your view here>; ((ViewPager) container).addView(root); views.put(position, root); return root; } @Override public void destroyItem(View collection, int position, Object o) { View view = (View)o; ((ViewPager) collection).removeView(view); views.remove(position); view = null; } |
。
然后通过重写
1 2 3 4 5 6 7 8 9 10 | @Override public void notifyDataSetChanged() { int key = 0; for(int i = 0; i < views.size(); i++) { key = views.keyAt(i); View view = views.get(key); <refresh view with new data> } super.notifyDataSetChanged(); } |
实际上,可以在
有同样的问题。对我来说,它可以扩展fragmentStatePageRadapter,并覆盖以下方法:
1 2 3 4 5 6 7 8 9 | @Override public Parcelable saveState() { return null; } @Override public void restoreState(Parcelable state, ClassLoader loader) { } |
。
在经历了数小时的挫折之后,我尝试了所有上述的解决方案来克服这个问题,也尝试了许多其他类似问题的解决方案,这些和这些问题都未能解决这个问题,也未能使
1)使
1 | public class myPagerAdapter extends FragmentPagerAdapter { |
2)为存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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)让
1 2 3 4 5 6 7 8 | private FragmentManager mFragmentManager; private ArrayList<PagerItem> mPagerItems; public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) { super(fragmentManager); mFragmentManager = fragmentManager; mPagerItems = pagerItems; } |
4)通过直接从
1 2 3 4 5 6 7 | 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)从容器
1 2 3 4 5 6 | 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(); |
希望有帮助。
我也有同样的问题,我的解决方案是凌驾于
1 2 3 4 | @Override public long getItemId(int position) { return mPages.get(position).getId(); } |
号
默认情况下,此方法返回项的位置。我假设
要使用它,每一页需要
我认为这是一种更一致的方式,而不是使用本主题中提到的视图标签作为解决方案。但可能不是所有的情况。
一种更简单的方法:使用
手术提出问题两年半后,这个问题仍然是一个问题。很明显,谷歌在这方面的优先权并不特别高,所以我找到了一个解决办法,而不是找到一个解决办法。对我来说,最大的突破是找出问题的真正原因(见本帖接受的答案)。一旦发现问题是任何活动页面没有正确刷新,我的解决方法就显而易见了:
在我的片段(页面)中:
- 我从OnCreateView中获取了所有填充表单的代码,并将其放入名为PopulateForm的函数中,该函数可以从任何地方调用,而不是由框架调用。此函数尝试使用get view获取当前视图,如果为空,则返回。PopulateForm只包含显示的代码是很重要的——创建FocusChange侦听器等的所有其他代码仍然在OnCreate中。
- 创建一个布尔值,该值可用作指示必须重新加载窗体的标志。我的是mbreloadform
- 如果设置了mbreloadform,则重写onresume()以调用populateform()。
在我的活动中,我执行页面加载的位置:
- 在更改任何内容之前转到第0页。我使用的是fragmentstatepageradapter,所以我知道最多影响两三页。更改为第0页可确保我只在第0、1和2页出现问题。
- 在清除旧列表之前,请使用它的大小()。这样您就可以知道有多少页面受到了这个bug的影响。如果大于3,将其减少到3-如果您使用的是不同的PageRadapter,则必须查看您需要处理的页面数(可能是全部?)
- 重新加载数据并调用pageadapter.notifyDataSetChanged()。
- 现在,对于每个受影响的页面,使用pager.getchildat(i)查看页面是否处于活动状态—这将告诉您是否有视图。如果是,请调用pager.populateView()。如果没有,请设置ReloadForm标志。
在此之后,当您重新加载第二组页面时,该错误仍然会导致一些页面显示旧数据。但是,它们现在将被刷新,您将看到新的数据-您的用户不会知道页面曾经是错误的,因为这种刷新将在他们看到页面之前发生。
希望这能帮助别人!
感谢Rui.Araujo和Alvaro Luis Bustamante。一开始,我试着用rui.araujo的方式,因为这很容易。它可以工作,但是当数据更改时,页面将明显地重新绘制。很糟糕,所以我试着用阿尔瓦罗·路易斯·布斯塔曼特的方式。它是完美的。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | @Override protected void onStart() { super.onStart(); } private class TabPagerAdapter extends PagerAdapter { @Override public int getCount() { return 4; } @Override public boolean isViewFromObject(final View view, final Object object) { return view.equals(object); } @Override public void destroyItem(final View container, final int position, final Object object) { ((ViewPager) container).removeView((View) object); } @Override public Object instantiateItem(final ViewGroup container, final int position) { final View view = LayoutInflater.from( getBaseContext()).inflate(R.layout.activity_approval, null, false); container.addView(view); ListView listView = (ListView) view.findViewById(R.id.list_view); view.setTag(position); new ShowContentListTask(listView, position).execute(); return view; } } |
。
当数据发生变化时:
1 2 3 4 5 6 7 | for (int i = 0; i < 4; i++) { View view = contentViewPager.findViewWithTag(i); if (view != null) { ListView listView = (ListView) view.findViewById(R.id.list_view); new ShowContentListTask(listView, i).execute(); } } |
所有这些解决方案对我没有帮助。因此我找到了一个可行的解决方案:你可以每次都做一次,但这还不够。您应该在更改适配器之前执行这些操作:
1 2 3 4 5 6 7 | FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); List<Fragment> fragments = fragmentManager.getFragments(); for (Fragment f : fragments) { transaction.remove(f); } transaction.commit(); |
在此之后:
1 | viewPager.setAdapter(adapter); |
。
我发现这个问题的决定很有趣。我们可以使用fragmentStatepageradapter(android.support.v4.app.fragmentStatepageradapter),而不是使用fragmentpageradapter(将所有片段保存在内存中),当我们选择它时,它每次都会重新加载片段。
两个适配器的实现是相同的。所以,我们只需要在"extend fragmentstatepageradapter"上更改"extend fragmentpageradapter"
如果有人使用基于fragmentstatepageradapter的适配器(这将允许viewpager创建显示所需的最小页面,我的情况下最多2个),@rui.araujo在适配器中覆盖getitemsposition的回答不会造成显著的浪费,但仍然可以改进。
在伪代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public int getItemPosition(Object object) { YourFragment f = (YourFragment) object; YourData d = f.data; logger.info("validate item position on page index:" + d.pageNo); int dataObjIdx = this.dataPages.indexOf(d); if (dataObjIdx < 0 || dataObjIdx != d.pageNo) { logger.info("data changed, discard this fragment."); return POSITION_NONE; } return POSITION_UNCHANGED; } |
在大量寻找这个问题之后,我找到了一个非常好的解决方案,我认为这是解决这个问题的正确方法。本质上,仅当视图被实例化时才调用InstantateItem,除非视图被销毁(这是重写getItemPosition函数以返回位置"无"时发生的情况)。相反,您要做的是保存创建的视图,并在适配器中更新它们,生成一个get函数以便其他人可以更新它,或者一个set函数更新适配器(我的最爱)。
因此,在MyViewPageRadapter中添加如下变量:
1 | private View updatableView; |
实例项中的:
1 2 3 4 5 6 | public Object instantiateItem(View collection, int position) { updatableView = new TextView(ctx); //My change is here view.setText(data.get(position)); ((ViewPager)collection).addView(view); return view; } |
号
这样,您就可以创建一个函数来更新视图:
1 2 3 4 | public updateText(String txt) { ((TextView)updatableView).setText(txt); } |
希望这有帮助!
我有一个类似的问题,我有四页,其中一页更新了其他三页的视图。我可以更新与当前页面相邻的页面上的小部件(参见工具栏、文本视图等)。在调用
为了解决我的问题,我在打电话给
在所有更新之后,我将使用
您可以在主更新页面的ListView中看到轻微的闪烁,但没有明显的闪烁。我还没有完全测试过,但它确实解决了我的直接问题。
我只是发布这个答案以防其他人发现它有用。为了做完全相同的事情,我只需从兼容性库中获取viewpager和pageradapter的源代码,并在代码中编译它(您需要整理所有错误并导入自己,但这绝对可以完成)。
然后,在customviewpager中,创建一个名为updateviewat(int position)的方法。视图本身可以从viewpager类中定义的arraylist斜接中获取(您需要在实例化项时为视图设置一个ID,并将此ID与updateViewat()方法中的位置进行比较)。然后您可以根据需要更新视图。
对我有好处的是1〔25〕。
在适配器中,将更新视图的代码放在
1 2 3 4 5 6 7 8 9 10 | @Override public int getItemPosition(Object object) { if (object instanceof YourViewInViewPagerClass) { YourViewInViewPagerClass view = (YourViewInViewPagerClass)object; view.setData(data); } return super.getItemPosition(object); } |
可能不是最正确的方法,但它起作用了(
我想,我知道viewpager的逻辑。
如果我需要刷新一组页面并基于新数据集显示它们,我将调用notifyDataSetChanged()。然后,viewpager对getitemPosition()进行了多次调用,并将片段作为对象传递到该位置。这个片段可以来自旧的数据集(我想丢弃它),也可以来自新的数据集(我想显示它)。所以,我重写getItemPosition(),在这里我必须确定我的片段是来自旧的数据集还是来自新的数据集。
在我的例子中,我有一个两窗格的布局,在左窗格上有一个顶部项目列表,在右窗格上有一个滑动视图(viewpager)。因此,我将一个链接存储到我的pageradapter中的当前顶级项目,以及每个实例化的页面片段中。当列表中选定的顶级项更改时,我将新的顶级项存储在PageRadapter中,并调用NotifyDataSetChanged()。在重写的getItemPosition()中,我将适配器中的顶级项与片段中的顶级项进行比较。只有当它们不相等时,我才返回位置"无"。然后,pageradapter重新检验返回位置"无"的所有碎片。
注意事项。最好是存储顶部项目ID而不是引用。
下面的代码片段有点示意性,但我根据实际工作的代码对其进行了调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class SomeFragment extends Fragment { private TopItem topItem; } public class SomePagerAdapter extends FragmentStatePagerAdapter { private TopItem topItem; public void changeTopItem(TopItem newTopItem) { topItem = newTopItem; notifyDataSetChanged(); } @Override public int getItemPosition(Object object) { if (((SomeFragment) object).getTopItemId() != topItem.getId()) { return POSITION_NONE; } return super.getItemPosition(object); } } |
感谢所有以前的研究人员!
您可以动态更新所有片段,您可以通过三个步骤看到。
在适配器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | public class MyPagerAdapter extends FragmentPagerAdapter { private static int NUM_ITEMS = 3; private Map<Integer, String> mFragmentTags; private FragmentManager mFragmentManager; public MyPagerAdapter(FragmentManager fragmentManager) { super(fragmentManager); mFragmentManager = fragmentManager; mFragmentTags = new HashMap<Integer, String>(); } // Returns total number of pages @Override public int getCount() { return NUM_ITEMS; } // Returns the fragment to display for that page @Override public Fragment getItem(int position) { switch (position) { case 0: return FirstFragment.newInstance(); case 1: return SecondFragment.newInstance(); case 2: return ThirdFragment.newInstance(); default: return null; } } // Returns the page title for the top indicator @Override public CharSequence getPageTitle(int position) { return"Page" + position; } @Override public Object instantiateItem(ViewGroup container, int position) { Object object = super.instantiateItem(container, position); if (object instanceof Fragment) { Fragment fragment = (Fragment) object; String tag = fragment.getTag(); mFragmentTags.put(position, tag); } return object; } public Fragment getFragment(int position) { Fragment fragment = null; String tag = mFragmentTags.get(position); if (tag != null) { fragment = mFragmentManager.findFragmentByTag(tag); } return fragment; }} |
号
现在在您的活动中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{ MyPagerAdapter mAdapterViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager); mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(mAdapterViewPager); viewPager.addOnPageChangeListener(this); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { Fragment fragment = mAdapterViewPager.getFragment(position); if (fragment != null) { fragment.onResume(); } } @Override public void onPageScrollStateChanged(int state) { }} |
号
最后,在您的片段中,类似这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class YourFragment extends Fragment { // newInstance constructor for creating fragment with arguments public static YourFragment newInstance() { return new YourFragment(); } // Store instance variables based on arguments passed @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } // Inflate the view for the fragment based on layout XML @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment, container, false); } @Override public void onResume() { super.onResume(); //to refresh your view refresh(); }} |
号
您可以在这里看到完整的代码。
感谢阿尔瓦罗·路易斯·巴斯塔曼特。
总是返回
我创建了一个库arrayPageRadapter来动态更改PageRadapter中的项目。
在内部,此库的适配器仅在必要时返回
您可以使用此库动态更改项目,如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @Override protected void onCreate(Bundle savedInstanceState) { /** ... **/ adapter = new MyStatePagerAdapter(getSupportFragmentManager() , new String[]{"1","2","3"}); ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter); adapter.add("4"); adapter.remove(0); } class MyPagerAdapter extends ArrayViewPagerAdapter<String> { public MyPagerAdapter(String[] data) { super(data); } @Override public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) { View v = inflater.inflate(R.layout.item_page, container, false); ((TextView) v.findViewById(R.id.item_txt)).setText(item); return v; } } |
号
Thils库还支持由片段创建的页面。
下面的代码对我有用。
创建一个类来扩展fragmentpageradapter类,如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | public class Adapter extends FragmentPagerAdapter { private int tabCount; private Activity mActivity; private Map<Integer, String> mFragmentTags; private FragmentManager mFragmentManager; private int container_id; private ViewGroup container; private List<Object> object; public Adapter(FragmentManager fm) { super(fm); } public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) { super(fm); mActivity = mA; mFragmentManager = fm; object = new ArrayList<>(); mFragmentTags = new HashMap<Integer, String>(); this.tabCount = numberOfTabs; } @Override public Fragment getItem(int position) { switch (position) { case 0: return Fragment0.newInstance(mActivity); case 1: return Fragment1.newInstance(mActivity); case 2: return Fragment2.newInstance(mActivity); default: return null; }} @Override public Object instantiateItem(ViewGroup container, int position) { Object object = super.instantiateItem(container, position); if (object instanceof Fragment) { Log.e("Already defined","Yes"); Fragment fragment = (Fragment) object; String tag = fragment.getTag(); Log.e("Fragment Tag","" + position +"," + tag); mFragmentTags.put(position, tag); }else{ Log.e("Already defined","No"); } container_id = container.getId(); this.container = container; if(position == 0){ this.object.add(0,object); }else if(position == 1){ this.object.add(1,object); }else if(position == 2){ this.object.add(2,object); } return object; } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); if (object instanceof Fragment) { Log.e("Removed" , String.valueOf(position)); } } @Override public int getItemPosition (Object object) { int index = 0; if(this.object.get(0) == object){ index = 0; }else if(this.object.get(1) == object){ index = 1; }else if(this.object.get(2) == object){ index = 2; }else{ index = -1; } Log.e("Index" ,"..................." + String.valueOf(index)); if (index == -1) return POSITION_NONE; else return index; } public String getFragmentTag(int pos){ return"android:switcher:"+R.id.pager+":"+pos; } public void NotifyDataChange(){ this.notifyDataSetChanged(); } public int getcontainerId(){ return container_id; } public ViewGroup getContainer(){ return this.container; } public List<Object> getObject(){ return this.object; } @Override public int getCount() { return tabCount; }} |
然后在您创建的每个片段中,创建一个updateFragment方法。在这个方法中,您可以更改片段中需要更改的内容。例如,在我的例子中,fragment0包含一个glsurfaceView,它基于.ply文件的路径显示一个3D对象,因此在我的updateFragment方法中,我更改了该ply文件的路径。
然后创建一个viewpager实例,
1 | viewPager = (ViewPager) findViewById(R.id.pager); |
。
以及一个适配器实例,
1 | adapter = new Adapter(getSupportFragmentManager(), 3, this); |
。
那就这么做,
1 2 | viewPager.setAdapter(adapter); viewPager.setOffscreenPageLimit(1); |
然后,在类内部初始化了上面的适配器类并创建了一个viewpager,每次要更新其中一个片段(在我们的例子中是fragment0)时,请使用以下方法:
1 2 3 4 5 6 7 8 9 | adapter.NotifyDataChange(); adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager. fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0. fragment0.updateFragment() method which include the updates on this fragment adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0. |
。
该解决方案基于阿尔瓦罗·路易斯·布斯塔曼特提出的技术。
您可以像这样在viewpager上添加寻呼机转换
1 | myPager.setPageTransformer(true, new MapPagerTransform()); |
在下面的代码中,当寻呼机滚动时,我在运行时更改了视图颜色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class MapPagerTransform implements ViewPager.PageTransformer { public void transformPage(View view, float position) { LinearLayout showSelectionLL=(LinearLayout)view.findViewById(R.id.showSelectionLL); if (position < 0) { showSelectionLL.setBackgroundColor(Color.WHITE); }else if (position >0){ showSelectionLL.setBackgroundColor(Color.WHITE); }else { showSelectionLL.setBackgroundColor(Color.RED); } } } |
。
这适用于像我这样需要从服务(或其他后台线程)更新viewpager的所有人,并且所有建议都无效:经过一点日志检查,我意识到notifyDataSetChanged()方法永远不会返回。getitemposition(object object)被称为all-ends-there,不需要进一步处理。然后我在父pageradapter类的文档中发现(不在子类的文档中),"数据集更改必须在主线程上发生,并且必须以调用notifyDataSetChanged()结束。"因此,本例中的工作解决方案是(使用FragmentStatePageRadapter和GetItemPosition(对象对象)设置为返回位置"无"):
然后调用notifyDataSetChanged():
1 2 3 4 5 6 | runOnUiThread(new Runnable() { @Override public void run() { pager.getAdapter().notifyDataSetChanged(); } }); |
你只需要这个简单的代码:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | private void updateAdapter(){ if(adapterViewPager != null){ int from = vpMyViewPager.getCurrentItem() - vpMyViewPager.getOffscreenPageLimit(); int to = vpMyViewPager.getCurrentItem() + vpMyViewPager.getOffscreenPageLimit(); vpMyViewPager.removeAllViews(); for(int i = from; i <= to; i++){ if(i < 0){ continue; } adapterViewPager.instantiateItem(vpMyViewPager, i); } } } |
。
说明:
如果你没有改变
重写getItemPosition()的正确方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Override public int getItemPosition(@NonNull Object object) { if (!(object instanceof MyPageView)) { // Should never happen return super.getItemPosition(object); } MyDataObject dataObject = ((MyPageView) object).getData(); if (!(dataObject instanceof MyDataObject)) { // Should never happen return super.getItemPosition(object); } if (lstItems.contains(dataObject)) { return POSITION_UNCHANGED; } return POSITION_NONE; } |
简而言之,您需要将用于绘制视图的数据附加/保存到视图中。从那里,当调用getItemPosition()时,viewPager可以确定当前显示的视图是否仍存在于适配器上,并相应地执行操作。
对我有用的是重写
1 2 3 4 5 6 7 8 9 | public class MyPagerAdapter extends FragmentPagerAdapter { ... @Override public void finishUpdate(ViewGroup container) { super.finishUpdate(container); this.notifyDataSetChanged(); } ... } |
。
值得一提的是,在Kitkat+上,如果你的
除了返回
根据我的经验,最好的解决方案是:https://stackoverflow.com/a/44177688/3118950,它覆盖
1.首先,必须在PageRadapter类中设置GetItemPosition方法。2.您必须阅读视图传呼机的确切位置。3.然后将该位置作为新位置的数据位置发送4.在setOnPageChange监听器中写入更新按钮onclick监听器
那个程序代码是我修改过的,只设置了特定的位置元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | public class MyActivity extends Activity { private ViewPager myViewPager; private List<String> data; public int location=0; public Button updateButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); data = new ArrayList<String>(); data.add("A"); data.add("B"); data.add("C"); data.add("D"); data.add("E"); data.add("F"); myViewPager = (ViewPager) findViewById(R.id.pager); myViewPager.setAdapter(new MyViewPagerAdapter(this, data)); updateButton = (Button) findViewById(R.id.update); myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i2) { //Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show(); } @Override public void onPageSelected( int i) { // here you will get the position of selected page final int k = i; updateViewPager(k); } @Override public void onPageScrollStateChanged(int i) { } }); } private void updateViewPager(final int i) { updateButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show(); data.set(i,"Replaced"+i); myViewPager.getAdapter().notifyDataSetChanged(); } }); } private class MyViewPagerAdapter extends PagerAdapter { private List<String> data; private Context ctx; public MyViewPagerAdapter(Context ctx, List<String> data) { this.ctx = ctx; this.data = data; } @Override public int getCount() { return data.size(); } @Override public int getItemPosition(Object object) { return POSITION_NONE; } @Override public Object instantiateItem(View collection, int position) { TextView view = new TextView(ctx); view.setText(data.get(position)); ((ViewPager)collection).addView(view); return view; } @Override public void destroyItem(View collection, int position, Object view) { ((ViewPager) collection).removeView((View) view); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Parcelable saveState() { return null; } @Override public void restoreState(Parcelable arg0, ClassLoader arg1) { } @Override public void startUpdate(View arg0) { } @Override public void finishUpdate(View arg0) { } } } |
号
我正在将Tablayout与ViewPageRadapter结合使用。对于在片段之间传递数据或在片段之间通信,请使用下面的代码,该代码工作良好,并且在片段出现时刷新它。在第二个片段的内部按钮点击写在代码下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String text=e1.getText().toString(); // get the text from EditText // move from one fragment to another fragment on button click TabLayout tablayout = (TabLayout) getActivity().findViewById(R.id.tab_layout); // here tab_layout is the id of TabLayout which is there in parent Activity/Fragment if (tablayout.getTabAt(1).isSelected()) { // here 1 is the index number of second fragment i-e current Fragment LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext()); Intent i = new Intent("EDIT_TAG_REFRESH"); i.putExtra("MyTextValue",text); lbm.sendBroadcast(i); } tablayout.getTabAt(0).select(); // here 0 is the index number of first fragment i-e to which fragment it has to moeve } }); |
。
below is the code which has to be written in first fragment (in my case) i-e in receiving Fragment.
号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | MyReceiver r; Context context; String newValue; public void refresh() { //your code in refresh. Log.i("Refresh","YES"); } public void onPause() { super.onPause(); LocalBroadcastManager.getInstance(context).unregisterReceiver(r); } public void onResume() { super.onResume(); r = new MyReceiver(); LocalBroadcastManager.getInstance(getActivity()).registerReceiver(r, new IntentFilter("EDIT_TAG_REFRESH")); } // this code has to be written before onCreateview() // below code can be written any where in the fragment private class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { PostRequestFragment.this.refresh(); String action = intent.getAction(); newValue=intent.getStringExtra("MyTextValue"); t1.setText(newValue); // upon Referesh set the text } } |
。
在我的例子中,每次片段进入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if(var == null){ var = new Something(); } if(list.isEmpty()){ list = new ArrayList<MyItem>(); } if(myAdapter == null){ myAdapter = new CustomAdapter(); recyclerView.setAdapter(myAdapter); } // etc... |
每次我们更改并在运行时从UI返回视图时,Fragment类都可以进入OnCreateView。
我把我自己的解决方案留在这里,这是一个变通办法,因为问题似乎是
(在我的例子中,我从未添加超过3个片段,但是您可以使用例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * this method is solving a bug in FragmentPagerAdapter which don't delete in the fragment manager any previous fragments in a ViewPager. * * @param containerId */ public void cleanBackStack(long containerId) { FragmentTransaction transaction = getFragmentManager().beginTransaction(); for (int i = 0; i < 3; ++i) { String tag ="android:switcher:" + containerId +":" + i; Fragment f = getFragmentManager().findFragmentByTag(tag); if (f != null) { transaction.remove(f); } } transaction.commit(); } |
我知道这是一个解决方法,因为如果框架创建标记的方式发生变化,它将停止工作。
(目前为
那么,使用它的方法是在获得容器之后:
1 2 | ViewPager viewPager = (ViewPager) view.findViewById(R.id.view_pager); cleanBackStack(viewPager.getId()); |
号
我想我已经做了一个简单的方法来通知数据集更改:
首先,稍微改变instantiatem函数的工作方式:
1 2 3 4 5 6 7 8 9 | @Override public Object instantiateItem(final ViewGroup container, final int position) { final View rootView = mInflater.inflate(...,container, false); rootView.setTag(position); updateView(rootView, position); container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mViewPager.setObjectForPosition(rootView, position); return rootView; } |
。
对于"updateView",用您想要填充的所有数据(settext、setbitmapimage,…)填充视图。
验证DestroyView的工作方式如下:
1 2 3 4 5 | @Override public void destroyItem(final ViewGroup container, final int position, final Object obj) { final View viewToRemove = (View) obj; mViewPager.removeView(viewToRemove); } |
。
现在,假设您需要更改数据,进行更改,然后调用pageradapter上的下一个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos, final NotifyLocation toPos) { final int offscreenPageLimit = viewPager.getOffscreenPageLimit(); final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit : mSelectedPhotoIndex + offscreenPageLimit; final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit : mSelectedPhotoIndex + offscreenPageLimit; if (fromPosInt <= toPosInt) { notifyDataSetChanged(); for (int i = fromPosInt; i <= toPosInt; ++i) { final View pageView = viewPager.findViewWithTag(i); mPagerAdapter.updateView(pageView, i); } } } public enum NotifyLocation { MOST_LEFT, CENTER, MOST_RIGHT } |
例如,如果您希望通知viewpager显示的所有视图发生了更改,可以调用:
1 | notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT); |
。
就这样。
这是一个可怕的问题,我很高兴提出一个优秀的解决方案:简单、高效和有效!
见下文,代码显示使用标志指示何时返回位置"无"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class ViewPagerAdapter extends PagerAdapter { // Members private boolean mForceReinstantiateItem = false; // This is used to overcome terrible bug that Google isn't fixing // We know that getItemPosition() is called right after notifyDataSetChanged() // Therefore, the fix is to return POSITION_NONE right after the notifyDataSetChanged() was called - but only once @Override public int getItemPosition(Object object) { if (mForceReinstantiateItem) { mForceReinstantiateItem = false; return POSITION_NONE; } else { return super.getItemPosition(object); } } public void setData(ArrayList<DisplayContent> newContent) { mDisplayContent = newContent; mForceReinstantiateItem = true; notifyDataSetChanged(); } } |
号
我在
在更新视图之前触发