[英]Android: re-use fragments of ViewPager & FragmentPagerAdapter
我在详细视图中使用ViewPager和FragmentPagerAdapter 。 这意味着我有一个项目列表,一个屏幕显示单个项目的详细信息。 但是,用户可以向左和向右滑动以浏览所有项目。 这遵循Google 浏览视图的指南 。
但我想知道一件事。 在ListView中,每行的视图都可以重复使用。 一旦一行滚出屏幕,它就会被重新用作绑定到ListView的适配器的getView方法的convertView参数。 但是这种重用行为似乎没有实现滑动视图。 这个例子说明了这个:
class DemoAdapter extends ArrayAdapter<DemoItem> {
public DemoAdapter(Context context, List<DemoItem> objects) {
super(context, 0, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// create a new view, otherwise re-use the existing convertView
LayoutInflater i = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = i.inflate(R.layout.list_item_demo, parent, false);
}
// get current item
DemoItem item = getItem(position);
if (item == null)
return convertView;
// update view with the item
TextView textTitle = (TextView)convertView.findViewById(R.id.demo_title);
if (textTitle != null)
textTitle.setText(item.getTitle());
return convertView;
}
}
但问题在于: FragmentPagerAdapter和FragmentStatePagerAdapter都在其getItem方法中创建片段(每个屏幕都是一个片段)。 但是它们不会将旧片段作为输入参数。 唯一的区别是, FragmentStatePagerAdapter会破坏未使用的片段。
public class DemoItemsPagerAdapter extends FragmentPagerAdapter {
private final Context context;
public DemoItemsPagerAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
// ToDo: get cursor or array of available items that can be swiped through
}
@Override
public Fragment getItem(int i) {
Fragment fragment = new DemoItemFragment();
// ToDo: initialize fragment by correct item
// ToDo: avoid creating too many fragments - try reusing them (but how?)
return fragment;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// the container is not the fragment, but the ViewPager itself
return super.instantiateItem(container, position);
}
@Override
public CharSequence getPageTitle(int position) {
// ToDo: return name for current entry
return null;
}
@Override
public int getCount() {
// ToDo: get count from cursor/array of available items
return 2;
}
}
那么,我该如何重用碎片呢? 实际上,只应调用getItems两次,因为一次只有一个片段可见,而第二个片段在用户滑动时开始转换。
更新:由于困惑,我创建了这个绘图。 它显示了适配器的行为。 除非设备内存不足,否则默认值会将所有片段保留在内存中。 一个应用程序处于后台或被杀死然后恢复每个片段将从其SavedInstanceState恢复。 第二个实现仅在内存中保留一些片段,但如果向左/向右滑动,则将从头开始再次完全创建被破坏的片段。 第三个实现是我正在寻求的。 您只有三个片段,然后在向左或向右滑动时重复使用。 因此片段A可以是位置1,2,3,4,5,6和7。
首先回答你的问题。
那么,我该如何重用碎片呢?
您可以在SparseArray中维护一个片段数组(当您需要将对象映射到整数时,它比HashMap更有内存效率)。
private SparseArray<BaseFragment> fragments;
所以你的代码在getItem(int i)
可以是这样的。
@Override
public Fragment getItem(int position) {
// Create a new fragment only when the fragment is not present in the fragments sparse
// array
BaseFragment fragment = fragments.get(position);
if (fragment == null) {
switch (position) {
case 0: {
fragment = new Fragment1();
fragments.put(0, fragment);
break;
}
case 1: {
fragment = new Fragment2();
fragments.put(1, fragment);
break;
}
.
.
.
default:
fragment = null;
}
}
return fragment;
}
在这里,我使用了一个BaseFragment,它几乎扩展了我想要使用的所有片段。
并且基于offScreenPageLimit调用AFAIK, getItem()
。 默认值为1.因此,基于此数字,将保留在内存中的片段将是
1 + 2*offScreenPageLimit // Current Page + 2 * left/right items
要么
1 + offScreenPageLimit // if its the first or last page.
更新1
您不必担心处理从内存中删除片段。 FragmentStatePagerAdapter会自动为您处理它们,如文档中所述 。
当存在大量页面时,此版本的寻呼机更有用,更像列表视图。 当页面对用户不可见时,它们的整个片段可能会被破坏,只保留该片段的保存状态。 与FragmentPagerAdapter相比,这允许寻呼机保持与每个被访问页面相关联的更少的存储器,代价是在页面之间切换时可能具有更多的开销。
当您使用较少的页面进行滑动时,通常在使用制表符或片段是静态时,可以使用FragmentPagerAdapter。 医生说,
此版本的寻呼机最适合在有少量通常更多静态片段进行分页时使用,例如一组选项卡。 用户访问的每个页面的片段将保留在内存中,但其视图层次结构可能在不可见时被销毁。 这可能导致使用大量内存,因为片段实例可以保持任意数量的状态。 对于较大的页面集,请考虑FragmentStatePagerAdapter。
更新2
您对案例2的上述定义是错误的。
第二个实现仅在内存中保留一些片段,但如果向左/向右滑动,则将从头开始再次完全创建被破坏的片段。
它不会从头开始创建,它将保存先前创建的片段的状态,并在创建新片段时使用相同的状态。 您可以在此处查看源代码 ,以便更好地了解其工作原理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.