繁体   English   中英

Android:重新使用ViewPager和FragmentPagerAdapter的片段

[英]Android: re-use fragments of ViewPager & FragmentPagerAdapter

我在详细视图中使用ViewPagerFragmentPagerAdapter 这意味着我有一个项目列表,一个屏幕显示单个项目的详细信息。 但是,用户可以向左和向右滑动以浏览所有项目。 这遵循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;
    }
}

但问题在于: FragmentPagerAdapterFragmentStatePagerAdapter都在其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。

FragmentPagerAdapter实现

首先回答你的问题。

那么,我该如何重用碎片呢?

您可以在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM