我正在使用兼容性库中的ViewPager。我已经成功地让它显示几个视图,我可以通过页面。
但是,我很难弄清楚如何用一组新的视图更新ViewPager。
我已经尝试了各种各样的事情,比如调用mAdapter.notifyDataSetChanged(), mviewpage .invalidate(),甚至在每次我想使用新的数据列表时创建一个全新的适配器。
没有任何帮助,textviews保持不变,从原始数据。
更新:
我做了一个小测试项目,我几乎能够更新视图。我将在下面粘贴这个类。
然而,似乎没有更新的是第二个视图,“B”仍然存在,它应该在按下更新按钮后显示“Y”。
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) {
}
}
}
我把我自己的解决方案留在这里,这是一个变通办法,因为问题似乎是FragmentPagerAdapter不清理之前的片段,你可以添加到ViewPager,在片段管理器。所以,在添加FragmentPagerAdapter之前,我创建了一个方法来执行:
(在我的情况下,我从来没有添加超过3个片段,但你可以使用例如getFragmentManager(). getbackstackentrycount(),并检查所有的片段。
/**
* 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();
}
我知道这是一种变通方法,因为如果框架创建标签的方式发生变化,它将停止工作。
(当前为"android:switcher:" + containerId + ":" + i ")
那么使用方法是在得到容器之后:
ViewPager viewPager = (ViewPager) view.findViewById(R.id.view_pager);
cleanBackStack(viewPager.getId());
This is for all those like me, which need to update the Viewpager from a service (or other background thread) and none of the proposals have worked:
After a bit of logchecking i realized, that the notifyDataSetChanged() method never returns. getItemPosition(Object object) is called an all ends there without further processing. Then i found in the docs of the parent PagerAdapter class (is not in the docs of the subclasses), "Data set changes must occur on the main thread and must end with a call to notifyDataSetChanged() ".
So, the working solution in this case was (using FragmentStatePagerAdapter
and getItemPosition(Object object) set to return POSITION_NONE) :
然后调用notifyDataSetChanged():
runOnUiThread(new Runnable() {
@Override
public void run() {
pager.getAdapter().notifyDataSetChanged();
}
});
我想,我有ViewPager的逻辑。
如果我需要刷新一组页面并基于新的数据集显示它们,我调用notifyDataSetChanged()。
然后,ViewPager对getItemPosition()进行多次调用,将Fragment作为对象传递给那里。这个片段可以来自旧数据集(我想要丢弃的数据集),也可以来自新数据集(我想要显示的数据集)。因此,我重写了getItemPosition(),在那里我必须以某种方式确定我的片段是来自旧数据集还是来自新数据集。
In my case I have a 2-pane layout with a list of top items on the left pane and a swipe view (ViewPager) on the right. So, I store a link to my current top item inside my PagerAdapter and also inside of each instantiated page Fragment.
When the selected top item in the list changes, I store the new top item in PagerAdapter and call notifyDataSetChanged(). And in the overridden getItemPosition() I compare the top item from my adapter to the top item from my fragment.
And only if they are not equal, I return POSITION_NONE.
Then, PagerAdapter reinstantiates all the fragments that have returned POSITION_NONE.
请注意。存储顶部项id而不是引用可能是一个更好的主意。
下面的代码片段有点示意图,但我从实际工作的代码中改编了它。
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);
}
}
感谢之前所有的研究人员!
有几种方法可以实现这一点。
第一种方法更简单,但效率更低。
重写PagerAdapter中的getItemPosition,如下所示:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
这样,当您调用notifyDataSetChanged()时,视图分页器将删除所有视图并重新加载它们。这样就得到了装填效果。
第二个选项是Alvaro Luis Bustamante(以前是alvarolb)建议的,在实例化一个新视图时使用instantiateItem()中的setTag()方法。然后,您可以使用findViewWithTag()来查找要更新的视图,而不是使用notifyDataSetChanged()。
结论
如果您有很多视图,或者希望支持修改任何特定的项和/或视图(在任何时候快速),那么第二种方法(标记)是非常灵活和高性能的,因为它可以防止重新创建所有未修改的视图。
(alvarolb的原创研究值得称赞。)
但如果你的应用只有“刷新”功能(甚至不允许单个条目的更改),或者只有很少的条目,请使用第一种方法,因为它节省了开发时间。