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

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

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

第一种实现

为了实现这一点,我们可以重写 FragmentPagerAdaptergetItemPosition() 方法,并让其总是返回 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详细介绍

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

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

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 中设置 ViewPagerFragmentStatePagerAdapter

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比较、区别

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

1. 内存管理

  • FragmentPagerAdapter

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

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

2. Fragment 生命周期

  • FragmentPagerAdapter

    • FragmentPagerAdapter 会保持所有 Fragment 的生命周期,这意味着它们的生命周期状态(如 onResumeonPause)不会改变,除非它们被销毁。
  • 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 不可见时释放内存资源。

当前文章价值1.59元,扫一扫支付后添加微信提供帮助!(如不能解决您的问题,可以申请退款)

你可能感兴趣的文章

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

资源分享

Genymotion启动虚拟设备上不了网,怎么办? Genymotion启动虚拟设备上不了网
SQLServer存储过程关于公用表表达式(CTE)和临时表的实例教程 SQLServer存储过程关于公用表
Python库flask实现激活码有效期过程实现 Python库flask实现激活码有效期
一键缓存清理工具 一键缓存清理工具

评论已关闭!