安卓FragmentStatePagerAdapter

24

我正在使用FragmentStatePagerAdapter,参考这个示例

MyAdapter类实现如下:

public static class MyAdapter extends FragmentStatePagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {
            return ArrayListFragment.newInstance(position);
        }
    }

ListFragment类包含以下方法以创建新实例:
    /**
     * Create a new instance of CountingFragment, providing "num"
     * as an argument.
     */
    static ArrayListFragment newInstance(int num) {
        ArrayListFragment f = new ArrayListFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

当我在活动中创建一个新的FragmentStatePagerAdapter时,会调用getItem方法,该方法又调用ListFragment类中的newInstance方法。当我想创建一个新的片段时,这非常好。
但是,如果需要,在用户从第2页翻到第1页时,如何修改getItem(甚至需要修改)以获取已经存在的片段对象并且我希望我的Activity检索先前创建的现有片段,以便它可以运行位于片段类中的内部类AsyncMethod,这对我来说不是很清楚。

2
getItem()名称有些奇怪,它并没有提供您想要的功能。请查看我在同一主题上的问题:https://dev59.com/B2kv5IYBdhLWcg3wdQvm - Michał Klimczak
这看起来非常有用。但是我正在使用ListFragment,并希望使用getSelectedItemPosition而不是自己制作getPosition。我需要再试验一下,看看能否弄清楚如何做到这一点。 - mraviator
我也使用了ListFragment,但它并不起作用。但是如果你找到了什么,我很乐意知道。 - Michał Klimczak
@mraviator,为什么不在你的ArrayListFragment中创建一个getInstance()方法来返回一个静态单例,而不是使用newInstance()呢? - IgorGanapolsky
我试图在我的片段中使用单例调用静态getInstance()来实现它。但是无法再次附加相同的片段。 - filthy_wizard
6个回答

44

我认为解决方案比提供的答案要简单得多。

如果您查看FragmentStatePagerAdapter.instantiateItem()源代码,您会注意到instantiateItem()已经为您处理了这个逻辑,因此您的getItem()实现应始终返回一个新实例。

因此,为了返回一个现有的Fragment,只需执行以下操作(假设您是从ViewPager中调用):

Fragment f = (Fragment) getAdapter().instantiateItem(this, getCurrentItem());

4
最佳答案藏在底部!!!public void onPageScrolled(int i, float v, int i2) { Fragment fragment = (Fragment)imagePagerAdapter.instantiateItem(mImageViewPager,i); - Ryan Heitner
2
只有在查看源代码后,我才意识到问题的逻辑,并发现这是页面上唯一合乎逻辑的答案。非常感谢。 - IT-Dan
这应该是被选中的答案(连同@RyanHeitner在何处使用此方法的评论)。谢谢。 - Firanto

9

虽然@imiric的解决方案非常简洁,但它仍然让我感到困扰,因为它需要了解类的实现。我的解决方案涉及将以下内容添加到您的适配器中,这应该可以很好地与FragmentStatePagerAdapter一起使用,可能会销毁片段:

    private SparseArray<WeakReference<Fragment>> mFragments = new SparseArray<>();

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment f = (Fragment) super.instantiateItem(container, position);
        mFragments.put(position, new WeakReference<>(f));  // Remember what fragment was in position
        return f;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        mFragments.remove(position);
    }

    public Fragment getFragment(int position) {
        WeakReference<Fragment> ref = mFragments.get(position);
        Fragment f = ref != null ? ref.get() : null;
        if (f == null) {
            Log.d(TAG, "fragment for " + position + " is null!");
        }
        return f;
    }

3
不应保留对Fragment的引用,因为它们可以在任何时候被销毁和重新创建。如果发生这种情况,您的引用将无效,并且Fragments不能被垃圾回收。 - wkarl
当然!不过将其更新为使用WeakReferences是否已足够? - qix

6

我已经实现了类似于您所要求的功能。我扩展了FragmentPagerAdapter类,代码如下:

public class ContactsFragmentPagerAdapter extends FragmentPagerAdapter {
    ActionBar mActionBar;
    private List<Fragment> mFragments;

    public ContactsFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        mFragments = fragments;
    }

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

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    public void setActionBar(ActionBar bar) {
        mActionBar = bar;
    }
}

注意,我在构造函数中添加了一个参数来传递List of Fragment对象。这样,该类的getItem()方法可以返回扩展Fragment或其子类之一的任何类,而不仅仅是像您所做的那样的一个特定类ArrayListFragment
在我实例化FragmentPagerAdapter的子类的Activity中,我已经传递了Fragment对象列表:
实例化FragmentPagerAdapter的类
public final class ContactManager extends Activity {
    private ContactsFragmentPagerAdapter mAdapter;
    private ViewPager mPager;
    public ActionBar mActionBar;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.contact_manager);

        List<Fragment> fragments = new Vector<Fragment>();
        fragments.add(Fragment.instantiate(this, ContactsListFragment.class.getName()));
        fragments.add(Fragment.instantiate(this, GroupsListFragment.class.getName()));
        mAdapter = new ContactsFragmentPagerAdapter(this.getFragmentManager(), fragments);

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

        mPager.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrollStateChanged(int arg0) {}

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {}

            @Override
            public void onPageSelected(int arg0) {
                mActionBar.getTabAt(arg0).select();
            }
        });

    }

}

通过访问变量“fragments”,您可以访问以前创建的Fragment,以便您可以运行该Fragment的方法。

19
如果有一种解决方案,不需要让所有片段都始终实例化以与“FragmentStatePagerAdapter”良好兼容,那将是很好的事情,因为这确实是该适配器的全部重点。 - blahdiblah

4
public class MyPagerAdapter extends FragmentStatePagerAdapter {
    final Context m_context;
    final WeakReference<Fragment>[] m_fragments;

    public DetailPagerAdapter(FragmentManager fm) {
        super(fm);
        m_context = ...
                m_fragments = new WeakReference[enter size here];
    }

    @Override
    public Fragment getItem(int position) {
        final Fragment fragment = instantiate your fragment;
        m_fragments[position] = new WeakReference<Fragment>(fragment);
        return fragment;
    }

    @Override
    public int getCount() {
        return ...
    }

    public Fragment getFragment(final int position) {
        return m_fragments[position] == null ? null :
            m_fragments[position].get();
    }
}

0

我认为这可能会有所帮助:

class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

    private val fragmentConstructors: List<() -> Fragment> by lazy {
        listOf(
                ::AuctionLotsFragment,
                ::ChatListFragment,
                ::MarketHomeFragment,
                ::WalletSectionsFragment,
                ::SettingsFragment
        )
    }

    private val fragments: HashMap<Int, WeakReference<Fragment>> by lazy {
        HashMap()
    }

    override fun getCount(): Int = fragmentConstructors.size

    override fun getItem(position: Int): Fragment {
        return fragments[position]?.get() ?: this.createFragmentForPosition(position)
    }

    private fun createFragmentForPosition(position: Int): Fragment {
        val fragment = fragmentConstructors[position].invoke()
        fragments[position] = WeakReference(fragment)
        return fragment
    }

}

0

不需要修改FragmentPagerAdapter,因为片段由FragmentManager缓存。所以你必须在其中找到它。使用此函数按pager适配器位置查找片段。

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

v4支持API的示例代码。


这个是在支持API的示例代码中包含的吗?还是你自己为支持API编写的?(如果它在那里,我至少会是“半官方”的) - Patrick
我自己编写了这个代码,基于Android API的源代码。我所说的“v4示例代码”是因为该代码使用了“getSupportFragmentManager”,而当您使用支持库时,它是可用的。如果您不使用“支持库”,则至少需要将“getSupportFragmentManager”更改为“getFragmentManager”。 FYI:http://developer.android.com/tools/extras/support-library.html - Daniel De León
1
谢谢。我希望他们能以某种方式正式宣布。这可能会在未来出现问题...嗯,不过还是谢谢! - Patrick
不用担心,因为Android基于Java哲学。"一次编写,永久运行"... - Daniel De León

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接