当前位置: 首页 > 知识库问答 >
问题:

Android:重用ViewPager的片段

汪建德
2023-03-14

我在一个详细视图中使用ViewPager和FragmentPagerAdapter。这意味着我有一个项目列表,一个屏幕显示单个项目的细节。但用户可以左右滑动来浏览所有项目。这遵循了谷歌滑动视图的指导方针。

但是我想知道一件事。在ListView中,每一行的视图都会被重用。一旦一行从屏幕上滚动出来,它就会被重用为绑定到ListView的适配器的getView方法的转换器视图参数。但是这种重用行为似乎并没有为滑动视图实现。这个例子说明了这一点:

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;
    }
}

但问题是:FragmentPageRadepter和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。

共有1个答案

端木明贤
2023-03-14

首先回答你的问题。

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

您可以在稀疏数组中维护一个片段数组(当您需要将对象映射到整数时,它比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,它几乎被我想使用的所有片段所扩展。

AFAIK,getItem()是基于offScreenPageLimit调用的。默认值为 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的定义是错误的。

第二个实现只在内存中保留一些片段,但是如果您向左/向右滑动,则会从头开始完全再次创建被破坏的片段。

它不会从头创建,它将保存以前创建的片段的状态,并在创建新片段时使用相同的状态。您可以在这里查看源代码,以更好地了解其工作原理

至于您的第三个实现,我建议覆盖适配器的默认行为,然后根据当前位置手动从适配器中删除/添加视图。

 类似资料:
  • 本文向大家介绍Android ViewPager基本用法和片段,包括了Android ViewPager基本用法和片段的使用技巧和注意事项,需要的朋友参考一下 示例 AViewPager允许在活动中显示多个片段,可通过向左或向右翻转来导航。甲ViewPager需要被馈送通过使用任一视图或片段的PagerAdapter。 但是,在使用片段分别为FragmentPagerAdapter和的情况下,有两

  • MainActivity(扩展了fragmentActivity)有listview,它调用listviewadapter,其中有一个带有ViewpagerAdapter的ViewPager。现在,如果我需要在ViewPager中生成5个片段,如何将getFragmentManager()传递给适配器。 MainActivity[fragmentActivity]---->Listview----

  • 解释 最近,我一直在与碎片和viewpager进行大量的斗争。 我发现ViewPager可以保存你想要的任意多的片段。当它首次实例化时,根据类型(vsFragmentPagerAdapter和FragmentStatePagerAdapter之间的差异),它将创建所有片段或只创建“需要的”片段。 您可以看到,返回null,尽管您是在被触发后调用此方法的。 如果您决定将保留的上下文强制转换为活动,以

  • 但问题是espresso看起来是所有的页面,而不仅仅是当前页面,我得到以下错误: Android.support.test.espresso.ambiguousViewMatcherException:“with id:eu.airpatrol.android:id/container_weather”匹配层次结构中的多个视图...

  • 有没有什么好的方法来引用用创建的片段?或者我应该如何从我的活动(准确地说是从选项菜单)中刷新查看页中的片段?