使用 FragmentPagerAdapter 时如何获取现有片段 [英] How to get existing fragments when using FragmentPagerAdapter

查看:25
本文介绍了使用 FragmentPagerAdapter 时如何获取现有片段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有问题让我的片段通过 Activity 相互通信,它使用 FragmentPagerAdapter,作为实现选项卡和所有细节管理的帮助类将 ViewPager 与关联的 TabHost 连接起来.我已经实现了 FragmentPagerAdapter,与 Android 示例项目 Support4Demos 提供的一样.

I have problem making my fragments communicating with each other through the Activity, which is using the FragmentPagerAdapter, as a helper class that implements the management of tabs and all details of connecting a ViewPager with associated TabHost. I have implemented FragmentPagerAdapter just as same as it is provided by the Android sample project Support4Demos.

主要问题是,当我既没有 Id 也没有 Tag 时,如何从 FragmentManager 获取特定片段?FragmentPagerAdapter 正在创建片段并自动生成 Id 和标签.

The main question is how can I get particular fragment from FragmentManager when I don't have neither Id or Tag? FragmentPagerAdapter is creating the fragments and auto generating the Id and Tags.

推荐答案

问题总结

注意:在这个答案中,我将参考 FragmentPagerAdapter 及其源代码.但是一般的解决方案也应该适用于FragmentStatePagerAdapter.

Summary of the problem

Note: In this answer I'm going to reference FragmentPagerAdapter and its source code. But the general solution should also apply to FragmentStatePagerAdapter.

如果您正在阅读本文,您可能已经知道 FragmentPagerAdapter/FragmentStatePagerAdapter 旨在为您的 ViewPager<创建 Fragments/code>,但是在 Activity 重新创建时(无论是来自设备旋转还是系统杀死您的应用程序以重新获得内存)这些 Fragment 不会再次创建,而是它们的 FragmentManager 检索的实例.现在假设您的 Activity 需要获得对这些 Fragments 的引用来处理它们.您没有这些创建的 Fragmentsidtag,因为 FragmentPagerAdapter 在内部设置它们.所以问题是如何在没有这些信息的情况下获得对它们的引用......

If you're reading this you probably already know that FragmentPagerAdapter/FragmentStatePagerAdapter is meant to create Fragments for your ViewPager, but upon Activity recreation (whether from a device rotation or the system killing your App to regain memory) these Fragments won't be created again, but instead their instances retrieved from the FragmentManager. Now say your Activity needs to get a reference to these Fragments to do work on them. You don't have an id or tag for these created Fragments because FragmentPagerAdapter set them internally. So the problem is how to get a reference to them without that information...

我在这个问题和类似问题上看到的很多解决方案都依赖于通过调用 FragmentManager.findFragmentByTag() 并模仿现有的 Fragment内部创建的标签:"android:switcher:" + viewId + ":" + id.这样做的问题是您依赖于内部源代码,众所周知,它不能保证永远保持不变.Google 的 Android 工程师可以轻松决定更改 tag 结构,这会破坏您的代码,使您无法找到对现有 Fragments 的引用.

A lot of the solutions I've seen on this and similar questions rely on getting a reference to the existing Fragment by calling FragmentManager.findFragmentByTag() and mimicking the internally created tag: "android:switcher:" + viewId + ":" + id. The problem with this is that you're relying on internal source code, which as we all know is not guaranteed to remain the same forever. The Android engineers at Google could easily decide to change the tag structure which would break your code leaving you unable to find a reference to the existing Fragments.

这里有一个简单的例子,说明如何获取对 FragmentPagerAdapter 返回的 Fragments 的引用,该引用不依赖于内部的 tags 集在 Fragments 上.关键是覆盖 instantiateItem() 并将引用保存在那里而不是 getItem().

Here's a simple example of how to get a reference to the Fragments returned by FragmentPagerAdapter that doesn't rely on the internal tags set on the Fragments. The key is to override instantiateItem() and save references in there instead of in getItem().

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

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

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

如果您更喜欢使用 tags 而不是类成员变量/对 Fragments 的引用,您也可以获取 FragmentPagerAdapter 以同样的方式设置标签:注意:这不适用于 FragmentStatePagerAdapter,因为它在创建 Fragments 时没有设置 tags.

or if you prefer to work with tags instead of class member variables/references to the Fragments you can also grab the tags set by FragmentPagerAdapter in the same manner: NOTE: this doesn't apply to FragmentStatePagerAdapter since it doesn't set tags when creating its Fragments.

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

请注意,此方法不依赖于模仿由 FragmentPagerAdapter 设置的内部 tag,而是使用适当的 API 来检索它们.这样,即使 tagSupportLibrary 的未来版本中发生变化,您仍然是安全的.

Note that this method does NOT rely on mimicking the internal tag set by FragmentPagerAdapter and instead uses proper APIs for retrieving them. This way even if the tag changes in future versions of the SupportLibrary you'll still be safe.

不要忘记,根据您的 Activity 的设计,您尝试处理的 Fragments 可能会也可能不会尚未存在,因此您必须在使用引用之前通过执行 null 检查来说明这一点.

Don't forget that depending on the design of your Activity, the Fragments you're trying to work on may or may not exist yet, so you have to account for that by doing null checks before using your references.

此外,如果您正在使用 FragmentStatePagerAdapter,那么您不想保留对 Fragments 的硬引用,因为您可能有很多,硬引用会不必要地将它们保留在内存中.而是将 Fragment 引用保存在 WeakReference 变量而不是标准变量中.像这样:

Also, if instead you're working with FragmentStatePagerAdapter, then you don't want to keep hard references to your Fragments because you might have many of them and hard references would unnecessarily keep them in memory. Instead save the Fragment references in WeakReference variables instead of standard ones. Like this:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}

这篇关于使用 FragmentPagerAdapter 时如何获取现有片段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆