动态更新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 position)的关系是什么?
目前我使用getItem来实例化片段,这是错误的吗? 但是,那么我必须将片段放入实例变量中,还是不要实现getItem()......? 我只是不明白这一点。
我试着阅读参考文献:
public abstract Fragment getItem(int position)
返回与指定位置关联的碎片。
公共对象instantiateItem(ViewGroup容器,int位置)
创建给定位置的页面。 适配器负责将视图添加到此处给出的容器,尽管它只能确保在从finishUpdate(ViewGroup)返回时完成此操作。 参数
容器包含将在其中显示页面的视图。 位置要实例化的页面位置。
返回
返回表示新页面的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在第一次显示之后从不销毁它。
我不建议您链接的帖子中提供的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中完全删除它们。
而不是从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
}
我遇到了这个问题并最终在今天解决了这个问题,所以我写下了我所学到的内容,希望对于Android的ViewPager
和我的更新有所帮助。 我在API级别17中使用FragmentStatePagerAdapter
,目前只有2个片段。 我认为一定有不对的地方,请纠正我,谢谢。
序列化数据必须加载到内存中。 这可以使用CursorLoader
/ AsyncTask
/ Thread
来完成。 是否自动加载取决于您的代码。 如果你使用的是CursorLoader
,它会自动加载,因为有一个注册数据观察者。
在调用viewpager.setAdapter(pageradapter)
,不断调用适配器的getCount()
来构建片段。 因此,如果数据被加载, getCount()
可以返回0,因此不需要为显示的数据创建虚拟片段。
加载数据后,由于getCount()
仍为0,因此适配器不会自动构建片段,因此我们可以将实际加载的数据编号设置为由getCount()
返回,然后调用适配器的notifyDataSetChanged()
。 ViewPager
开始通过内存中的数据创建片段(只是前两个片段)。 它在返回notifyDataSetChanged()
之前完成。 然后ViewPager
拥有你需要的正确的片段。
如果数据库和内存中的数据都更新(通过写入),或者仅更新内存中的数据(写回),或者只更新数据库中的数据。 在最后两种情况下,如果数据不是从数据库自动加载到内存(如上所述)。 ViewPager
和寻呼机适配器只处理内存中的数据。
所以当内存中的数据更新时,我们只需要调用适配器的notifyDataSetChanged()
。 既然已经创建了片段,适配器的onItemPosition()
将之前被称为notifyDataSetChanged()
返回,没有什么需要做getItemPosition()
然后数据被更新。