71

我有一个浏览器,可以浏览片段。我的FragmentPagerAdapter子类在方法中创建了一个新片段,getItem这似乎很浪费。是否有与inFragmentPagerAdapter等价的方法可以让我重用已经创建的片段?我的代码如下。convertViewlistAdapter

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

6 回答 6

102

已经为您FragmentPagerAdapter缓存了。Fragments每个片段都分配有一个标签,然后FragmentPagerAdapter尝试调用findFragmentByTag. 它仅getItem在结果为时调用。因此,您不必自己缓存片段。findFragmentByTagnull

于 2011-08-17T19:05:54.327 回答
67

Geoff 帖子的附录:

您可以参考您的Fragmentin FragmentPagerAdapterusing findFragmentByTag()。标签的名称是这样生成的:

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

于 2012-01-22T23:18:12.423 回答
24

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

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


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

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

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

这是一个简单示例,说明如何获取对不依赖Fragments于. 关键是在其中覆盖和保存引用而不是在.FragmentPagerAdaptertagsFragmentsinstantiateItem()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:注意:这不适用,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...
}
于 2015-03-26T19:40:09.290 回答
17

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

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

v4 支持 api 的示例代码。

于 2013-06-18T06:09:12.973 回答
1

给未来的读者!

如果您正在考虑使用 viewpager 重用片段,最好的解决方案是使用ViewPager 2,因为View Pager 2 使用了 RecyclerView。

于 2019-09-03T06:42:31.337 回答
0

我知道这(理论上)不是问题的答案,而是一种不同的方法。

我遇到了需要刷新可见片段的问题。无论我尝试什么,都失败了,惨遭失败……

在尝试了这么多不同的事情之后,我终于用BroadCastReceiver. 当您需要对可见片段做某事并在片段中捕获它时,只需发送广播即可。

如果您还需要某种响应,也可以通过广播发送。

干杯

于 2015-05-02T18:11:29.120 回答