在fragmentpageradapter中重用片段

时间:2011-08-07 21:50:51

标签: android android-fragments fragment android-compatibility

我有一个浏览片段的viewpager。我的FragmentPagerAdapter子类在getItem方法中创建了一个看似浪费的新片段。是否FragmentPagerAdapterconvertView中的listAdapter等效,可以重复使用已创建的片段?我的代码如下。

public class ProfilePagerAdapter extends FragmentPagerAdapter {

    ArrayList<Profile> mProfiles = new ArrayList<Profile>();

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

    /**
     * Adding a new profile object created a new page in the pager this adapter is set to.
     * @param profile
     */
    public void addProfile(Profile profile){
        mProfiles.add(profile);
    }

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

    @Override
    public Fragment getItem(int position) {
        return new ProfileFragment(mProfiles.get(position));
    }
}

5 个答案:

答案 0 :(得分:100)

FragmentPagerAdapter已为您缓存Fragments。每个片段都分配了一个标记,然后FragmentPagerAdapter尝试调用findFragmentByTag。如果getItem的结果为findFragmentByTag,则只会调用null。所以你不应该自己缓存片段。

答案 1 :(得分:67)

杰夫的帖子附录:

您可以使用findFragmentByTag()引用Fragment中的FragmentPagerAdapter。标签的名称以这种方式生成:

private static String makeFragmentName(int viewId, int index)
{
     return "android:switcher:" + viewId + ":" + index;
}

其中viewId是ViewPager的id

请看这个链接:http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104

答案 2 :(得分:23)

似乎很多人在查看此问题时都在寻找一种方法来引用由Fragments / FragmentPagerAdapter创建的FragmentStatePagerAdapter。我想提供我的解决方案,而不依赖于内部创建的tags,其他答案就在这里使用。

作为奖励,此方法也适用于FragmentStatePagerAdapter。有关详细信息,请参阅下面的注释。


当前解决方案的问题:依赖内部代码

我在此问题和类似问题上看到的很多解决方案都依赖于通过调用Fragment和模仿internally created tag: "android:switcher:" + viewId + ":" + id来获取对现有FragmentManager.findFragmentByTag()的引用。这样做的问题在于你依赖于内部源代码,我们都知道并不能保证它们永远保持不变。 Google的Android工程师可以轻松决定更改tag结构,这会破坏您的代码,导致您无法找到对现有Fragments的引用。

替代解决方案,不依赖于内部tag

以下是一个简单的示例,说明如何获取Fragments返回的FragmentPagerAdapter的引用,该引用不依赖于tags上的内部Fragments集。关键是覆盖instantiateItem()并保存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的类成员变量/引用,您还可以获取tags设置的FragmentPagerAdapter 1}}以同样的方式: 注意:这不适用于FragmentStatePagerAdapter,因为它在创建tags时未设置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;
}

请注意,此方法不依赖于模仿tag设置的内部FragmentPagerAdapter,而是使用适当的API来检索它们。这种方式即使tag的未来版本中的SupportLibrary更改,您仍然可以安全。


不要忘记,根据您Activity的设计,您尝试工作的Fragments可能存在,也可能不存在,所以你有在使用您的参考文献之前,通过null检查来解释这一点。

此外,如果相反您正在使用FragmentStatePagerAdapter,那么您不希望保留对Fragments的硬性引用,因为您可能拥有其中许多内容硬引用会不必要地将它们留在内存中。而是将Fragment个引用保存在WeakReference个变量而不是标准变量中。像这样:

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

答案 3 :(得分:17)

如果片段仍在内存中,您可以使用此功能找到它。

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

v4支持api的示例代码。

答案 4 :(得分:0)

供将来的读者使用!

如果您想通过viewpager重用片段,最好的解决方案是使用ViewPager 2,因为 View Pager 2使用了RecyclerView。