在fragmentpageradapter中重用片段

发布于 2024-11-28 12:11:06 字数 863 浏览 5 评论 0原文

我有一个可以浏览片段的视图分页器。我的 FragmentPagerAdapter 子类在 getItem 方法中创建了一个新片段,这似乎很浪费。是否有一个与 listAdapter 中的 convertView 等效的 FragmentPagerAdapter 使我能够重用已创建的片段?我的代码如下。

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

I have a viewpager that pages through fragments. My FragmentPagerAdapter subclass creates a new fragment in the getItem method which seems wasteful. Is there a FragmentPagerAdapter equivalent to the convertView in the listAdapter that will enable me to reuse fragments that have already been created? My code is below.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

缱倦旧时光 2024-12-05 12:11:06

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

The FragmentPagerAdapter already caches the Fragments for you. Each fragment is assigned a tag, and then the FragmentPagerAdapter tries to call findFragmentByTag. It only calls getItem if the result from findFragmentByTag is null. So you shouldn't have to cache the fragments yourself.

窝囊感情。 2024-12-05 12:11:06

Geoff 帖子的附录:

您可以使用 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

Appendix for Geoff's post:

You can get reference to your Fragment in FragmentPagerAdapter using findFragmentByTag(). The name of the tag is generated this way:

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

where viewId is id of ViewPager

Look at this link: http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104

荭秂 2024-12-05 12:11:06

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

作为奖励,此方法还应该与 FragmentStatePagerAdapter 一起使用。有关更多详细信息,请参阅下面的注释。


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

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

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

下面是一个简单的示例,说明如何获取对 FragmentPagerAdapter 返回的 Fragments 的引用,该引用不依赖于内部 tag Fragments 上设置的内部 tags。关键是覆盖 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();
        }
    }
}

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

@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 的未来版本中发生更改,您仍然是安全的。


不要忘记,根据您的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...
}

Seems a lot of the people viewing this question are looking for a way to reference the Fragments created by FragmentPagerAdapter/FragmentStatePagerAdapter. I would like to offer my solution to this without relying on the internally created tags that the other answers on here use.

As a bonus this method should also work with FragmentStatePagerAdapter. See notes below for more detail.


Problem with current solutions: relying on internal code

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.

Alternate solution without relying on internal tag

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

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

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.


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.

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...
}
清醇 2024-12-05 12:11:06

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

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

v4 支持 api 的示例代码。

If the fragment still in memory you can find it with this function.

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

Sample code for v4 support api.

绝對不後悔。 2024-12-05 12:11:06

对于未来的读者!

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

For future readers!

If you are thinking of reusing fragments with viewpager, best solution is to use ViewPager 2, since View Pager 2 make use of RecyclerView.

歌枕肩 2024-12-05 12:11:06

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

我遇到一个问题,需要刷新可见片段。无论我尝试过什么,都失败了,而且失败得很惨……

在尝试了很多不同的事情之后,我终于使用 BroadCastReceiver 完成了这个任务。当您需要对可见片段执行某些操作并在片段中捕获它时,只需发送广播即可。

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

干杯

I know this is (theoretically) not an answer to the question, but a different approach.

I had an issue where I needed to refresh the visible fragments. Whatever I tried, failed and failed miserably...

After trying so many different things, I have finally finish this using BroadCastReceiver. Simply send a broadcast when you need to do something with the visible fragments and capture it in the fragment.

If you need some kind of a response as well, you can also send it via broadcast.

cheers

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文