003-使用FragmentPagerAdapter创建的Fragment在ViewPager切换过程中不被销毁的实现

2024-09-14 17:08 003-使用FragmentPagerAdapter创建的Fragment在ViewPager切换过程中不被销毁的实现已关闭评论

在使用 FragmentPagerAdapter 创建 Fragment 时,默认情况下,当 Fragment 不再可见时,FragmentPagerAdapter 会将其销毁以节省资源。然而,有时候我们可能希望某些 Fragment 始终保持创建状态而不被销毁,特别是在这些 Fragment 中维护了一些重要的状态信息时。

第一种实现

为了实现这一点,我们可以重写 FragmentPagerAdapter 的 getItemPosition() 方法,并让其总是返回 POSITION_NONE。这样做会让 FragmentPagerAdapter 认为每次调用 getItem() 时都需要创建一个新的 Fragment,但实际上我们可以返回同一个 Fragment 实例。这种方法虽然有效,但它并不是最佳实践,因为它可能会导致性能问题,因为 getItem() 方法会被频繁调用。

第二种实现

一个更好的做法是继承自 FragmentStatePagerAdapter,并重写 isViewFromObject() 方法来判断是否应该保留 Fragment。不过,FragmentStatePagerAdapter 默认的行为就是不销毁 Fragment,直到它们不再被需要为止,通常不需要重写 isViewFromObject()。

下面是一个示例代码,展示了如何使用 FragmentStatePagerAdapter 来实现 Fragment 的持久化:

public class MyFragmentStatePagerAdapter extends FragmentStatePagerAdapter {

    private final List<Fragment> fragmentList = new ArrayList<>();
    private final List<String> titleList = new ArrayList<>();

    public MyFragmentStatePagerAdapter(FragmentManager fm) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titleList.get(position);
    }

    public void addFragment(Fragment fragment, String title) {
        fragmentList.add(fragment);
        if (title != null) {
            titleList.add(title);
        }
        notifyDataSetChanged();
    }
}

在这个例子中,MyFragmentStatePagerAdapter 继承自 FragmentStatePagerAdapter,并且在 getItem() 方法中返回了一个 Fragment 列表中的元素。Fragment 会在列表中被保存,直到 FragmentStatePagerAdapter 不再需要它们为止。

注意,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 表示只有当前选中的 Fragment 会调用 onResume() 方法,这有助于提高性能。

第三种实现

确实需要一个 FragmentPagerAdapter 来保持 Fragment 不被销毁,还可以按照以下方式实现:

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    private final List<Fragment> fragmentList = new ArrayList<>();
    private final List<String> titleList = new ArrayList<>();

    public MyFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        // 将Fragment添加到集合中,以保持其存活
        if (!fragmentList.contains(fragment)) {
            fragmentList.add(fragment);
        }
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // 不销毁Fragment
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE; // 告诉ViewPager总是创建一个新的Fragment
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titleList.get(position);
    }

    public void addFragment(Fragment fragment, String title) {
        fragmentList.add(fragment);
        if (title != null) {
            titleList.add(title);
        }
        notifyDataSetChanged();
    }
}

这种方法虽然可以达到目的,但不如直接使用 FragmentStatePagerAdapter 那么高效和简洁。使用 FragmentStatePagerAdapter 是更推荐的做法。

FragmentStatePagerAdapter详细介绍

FragmentStatePagerAdapter 是 ViewPager 的一个适配器,允许在多个 Fragment 之间进行滑动切换。与 FragmentPagerAdapter 相比,FragmentStatePagerAdapter 更适合用于数量较多的 Fragment 场景,因为它仅会保存当前可见的 Fragment 的状态以及下一个将要显示的 Fragment 的状态。当 Fragment 不再需要时,它们的状态会被保存,而 Fragment 本身会被销毁。

下面是一个详细的示例,说明如何使用 FragmentStatePagerAdapter 来创建一个可以动态添加 Fragment 的 ViewPager。

1. 创建 Fragment 类

创建一个或多个 Fragment 类。

public class ExampleFragment extends Fragment {

    private static final String ARG_SECTION_NUMBER = "section_number";

    public static ExampleFragment newInstance(int sectionNumber) {
        ExampleFragment fragment = new ExampleFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_example, container, false);
        TextView textView = rootView.findViewById(R.id.section_label);
        textView.setText("Section " + getArguments().getInt(ARG_SECTION_NUMBER));
        return rootView;
    }
}

这个 Fragment 有一个静态工厂方法 newInstance,它接受一个整数参数,并将其作为参数传递给 Fragment。在 onCreateView 方法中,我们根据传入的参数显示相应的文本。

2. 创建 FragmentStatePagerAdapter

接下来,创建一个 FragmentStatePagerAdapter 的子类,用于向 ViewPager 提供 Fragment:

public class SectionsPagerAdapter extends FragmentStatePagerAdapter {

    private Context mContext;

    public SectionsPagerAdapter(FragmentManager fm, Context context) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        mContext = context;
    }

    @Override
    public Fragment getItem(int position) {
        // 返回相应的Fragment实例
        return ExampleFragment.newInstance(position + 1);
    }

    @Override
    public int getCount() {
        // 返回Fragment的数量
        return 3; // 示例中假定有三个Fragment
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mContext.getResources().getStringArray(R.array.section_titles)[position];
    }
}

在这个适配器中,getItem 方法返回一个 Fragment 的实例,getCount 方法返回 Fragment 的总数。getPageTitle 方法返回每个 Fragment 的标题。

3. 在 Activity 中使用 ViewPager

最后,在 Activity 中设置 ViewPager 和 FragmentStatePagerAdapter:

public class MainActivity extends AppCompatActivity {

    private SectionsPagerAdapter mSectionsPagerAdapter;
    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 设置适配器
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), this);

        // 设置ViewPager
        mViewPager = findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        // 设置TabLayout
        TabLayout tabLayout = findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);

        // 设置TabLayout的Tab标题
        tabLayout.getTabAt(0).setText("SECTION 1");
        tabLayout.getTabAt(1).setText("SECTION 2");
        tabLayout.getTabAt(2).setText("SECTION 3");
    }
}

在这个 Activity 中,我们创建了一个 ViewPager 并设置了 SectionsPagerAdapter 作为它的适配器。我们还添加了一个 TabLayout 来显示 Fragment 的标题,并将其与 ViewPager 关联起来。

需要注意事项

  • FragmentStatePagerAdapter 只会保留当前可见的 Fragment 和下一个即将可见的 Fragment 的状态。这意味着,如果 Fragment 数量很多,并且用户来回滑动多次,旧的 Fragment 会被销毁并重新创建。
  • BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 参数确保只有当前可见的 Fragment 处于 RESUMED 状态,这有助于节省资源。
  • 如果需要动态添加或移除 Fragment,需要手动调整 ViewPager 的内容,并通知适配器数据集的变化。

FragmentPagerAdapter和FragmentStatePagerAdapter比较、区别

FragmentPagerAdapter 和 FragmentStatePagerAdapter 都是用来管理 ViewPager 中 Fragment 的适配器类,但它们在内存管理和生命周期控制上有显著的区别。下面将详细比较这两种适配器的不同之处:

1. 内存管理

  • FragmentPagerAdapter:

    • FragmentPagerAdapter 会将所有创建的 Fragment 保留在内存中,即使它们当前不可见。这是因为 FragmentPagerAdapter 通过调用 detach 方法来隐藏 Fragment,而不是销毁它们。这种方法适用于需要管理少量 Fragment 的场景,因为可以避免频繁创建和销毁 Fragment 的开销。
  • FragmentStatePagerAdapter:

    • FragmentStatePagerAdapter 会销毁不可见的 Fragment,仅保留它们的状态。当用户再次滑动到这些 Fragment 时,它们将被重新创建。这种方法适用于需要处理大量 Fragment 的情况,因为它能够节省内存。

2. Fragment 生命周期

  • FragmentPagerAdapter:

    • FragmentPagerAdapter 会保持所有 Fragment 的生命周期,这意味着它们的生命周期状态(如 onResume、onPause)不会改变,除非它们被销毁。
  • FragmentStatePagerAdapter:

    • FragmentStatePagerAdapter 会在 Fragment 不可见时销毁它们,并保存它们的状态。当用户再次访问这些 Fragment 时,它们的状态会被恢复,但它们会经历完整的生命周期,包括重新创建、初始化等过程。

3. 性能和内存使用

  • FragmentPagerAdapter:

    • 由于它会保存所有 Fragment 的实例,因此可能会导致较高的内存消耗。但如果 Fragment 数量有限,这种方法可以提供更快的响应速度,因为不需要重新创建 Fragment。
  • FragmentStatePagerAdapter:

    • 相对于 FragmentPagerAdapter,它更适合处理大量 Fragment 的场景,因为它会销毁不可见的 Fragment,从而减少内存占用。虽然这种方法可能会在用户切换 Fragment 时带来一些延迟,但它更适合内存敏感的应用程序。

4. 实现方式

  • FragmentPagerAdapter:

    • 需要实现 getItem(int) 和 getCount() 方法。getItem(int) 返回指定位置的 Fragment 实例,而 getCount() 返回 Fragment 的总数。
  • FragmentStatePagerAdapter:

    • 同样需要实现 getItem(int) 和 getCount() 方法。getItem(int) 返回指定位置的 Fragment 实例,而 getCount() 返回 Fragment 的总数。

5. 适用场景

  • FragmentPagerAdapter:

    • 适用于少量的 Fragment,如一组选项卡。
  • FragmentStatePagerAdapter:

    • 适用于大量的 Fragment 或者 Fragment 内容动态变化的情况。

总结

选择 FragmentPagerAdapter 还是 FragmentStatePagerAdapter 取决于你的应用场景:

  • 如果你的应用中 Fragment 的数量较少,并且这些 Fragment 需要在后台持续保持活跃状态,那么 FragmentPagerAdapter 是一个合适的选择。
  • 如果你的应用需要处理大量的 Fragment,或者你关心内存使用效率,那么 FragmentStatePagerAdapter 会更合适,因为它能够在 Fragment 不可见时释放内存资源。

你可能感兴趣的文章

来源:每日教程, 每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://teachcourse.cn/3677.html ,谢谢支持!

资源分享

Ubuntu更新python3 Ubuntu更新python3
关于nginx防盗链配置中排除url特定字符串的总结 关于nginx防盗链配置中排除url特
关于如何解决“NoClassDefFoundError”错误的问题? 关于如何解决“NoClassDefFo
harmony学习AppStorage在多个UIAbility组件之间的应用 harmony学习AppStorage在多个U

评论已关闭!