我无法更新 ViewPager 中的内容。
FragmentPagerAdapter 类中方法 instantiateItem() 和 getItem() 的正确用法是什么?
我只使用 getItem() 来实例化并返回我的片段:
@Override public Fragment getItem(int position) { return new MyFragment(context, paramters); }
这运作良好。除了我不能改变内容。
所以我发现了这个:ViewPager PagerAdapter not updates the View
“我的方法是在 instantiateItem() 方法中对任何实例化视图使用 setTag() 方法”
现在我想实现 instantiateItem() 来做到这一点。但我不知道我必须返回什么(类型是 Object)以及与 getItem(int position) 的关系是什么?
我阅读了参考资料:
公共抽象片段 getItem(int 位置) 返回与指定位置关联的 Fragment。 public Object instantiateItem(ViewGroup 容器,int 位置) 为给定位置创建页面。适配器负责将视图添加到此处给出的容器中,尽管它只必须确保在它从 finishUpdate(ViewGroup) 返回时完成此操作。参数 容器 将在其中显示页面的包含视图。position 要实例化的页面位置。 退货 返回一个表示新页面的对象。这不需要是视图,但可以是页面的其他容器。
返回与指定位置关联的 Fragment。
为给定位置创建页面。适配器负责将视图添加到此处给出的容器中,尽管它只必须确保在它从 finishUpdate(ViewGroup) 返回时完成此操作。参数
容器 将在其中显示页面的包含视图。position 要实例化的页面位置。
退货
返回一个表示新页面的对象。这不需要是视图,但可以是页面的其他容器。
但我还是不明白。
这是我的代码。我正在使用支持包 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); } }
我的片段适配器
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; } }
我的片段
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; } }
在使用 FragmentPagerAdapter 或 FragmentStatePagerAdapter 时,最好单独处理,完全getItem()不接触instantiateItem()。PagerAdapter 上的instantiateItem()- destroyItem()-isViewFromObject()接口是一个较低级别的接口,FragmentPagerAdapter 使用它来实现更简单的getItem()接口。
getItem()
instantiateItem()
destroyItem()
isViewFromObject()
在进入这个之前,我应该澄清一下
如果要切换出正在显示的实际片段,则需要避免使用 FragmentPagerAdapter 并使用 FragmentStatePagerAdapter。
此答案的早期版本错误地将 FragmentPagerAdapter 用作其示例 - 这将不起作用,因为 FragmentPagerAdapter 在第一次显示后永远不会破坏片段。
我不推荐您链接的帖子中提供的解决方法setTag()。findViewWithTag()正如您所发现的,使用setTag()和findViewWithTag()不适用于片段,所以它不是一个很好的匹配。
setTag()
findViewWithTag()
正确的解决方案是覆盖getItemPosition(). 当notifyDataSetChanged()被调用时,ViewPager 会调用getItemPosition()其适配器中的所有项目,以查看它们是否需要移动到不同的位置或移除。
getItemPosition()
notifyDataSetChanged()
默认情况下,getItemPosition()返回POSITION_UNCHANGED,这意味着,“这个对象在它所在的地方很好,不要破坏或删除它。” 返回POSITION_NONE解决了这个问题,而是说,“这个对象不再是我正在显示的项目,删除它。” 因此,它具有删除和重新创建适配器中每个项目的效果。
POSITION_UNCHANGED
POSITION_NONE
这是一个完全合法的修复!此修复使 notifyDataSetChanged 的行为类似于常规适配器,无需视图回收。如果您实施此修复并且性能令人满意,那么您就可以参加比赛了。任务完成。
如果您需要更好的性能,您可以使用更高级的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(),从而自行管理更新。您不必getItem()像在 ListView 或其他 AdapterView 类型的适配器中那样将所有更新代码放入其中。
onCreate()
onDestroy()
最后一件事——仅仅因为 FragmentPagerAdapter 不破坏片段并不意味着 getItemPosition 在 FragmentPagerAdapter 中完全没用。您仍然可以使用此回调在 ViewPager 中重新排序您的片段。但是,它永远不会将它们完全从 FragmentManager 中删除。