Android FragmentStatePagerAdapter,如何标记片段以便以后找到它

时间:2012-09-12 09:22:42

标签: android android-viewpager

使用FragmentStatePageAdapter时,我会得到这样的片段:

    @Override
    public Fragment getItem(int position) {
        return new SuperCoolFragment();
    }

然而,稍后我的代码我需要找到这个片段来设置属性。在应用程序的其他部分,我使用片段我基本上标记它们并使用findFragmentByTag(TAG)查找它们,但现在我不知道该怎么做。

如何使用FragmentStatePageAdapter找到片段?

9 个答案:

答案 0 :(得分:46)

*编辑*

@ElliotM指出,有一个更好的解决方案。无需更改适配器中的任何内容,只需获取片段:

Fragment myFragment = (Fragment) adapter.instantiateItem(viewPager, viewPager.getCurrentItem());

*旧解决方案*

最佳选择是第二个解决方案: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

简而言之:您可以跟踪所有“活动”片段页面。在这种情况下,您将跟踪ViewPager使用的FragmentStatePagerAdapter中的片段页面。

public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferenceMap.put(index, myFragment);
    return myFragment;
}

为了避免保留对“非活动”片段页面的引用,我们需要实现FragmentStatePagerAdapter的destroyItem(...)方法:

public void destroyItem(View container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferenceMap.remove(position);
}

...当您需要访问当前可见的页面时,请调用:

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

... MyAdapter的getFragment(int)方法如下所示:

public MyFragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}

---编辑:

同样在适配器中添加此内容,以便在更改方向后添加:

/**
 * After an orientation change, the fragments are saved in the adapter, and
 * I don't want to double save them: I will retrieve them and put them in my
 * list again here.
 */
@Override
public Object instantiateItem(ViewGroup container, int position) {
    MyFragment fragment = (MyFragment) super.instantiateItem(container,
            position);
    mPageReferenceMap.put(position, fragment);
    return fragment;
}

答案 1 :(得分:15)

我找到了另一种方法,不确定使用它是否有任何问题,对我来说似乎没问题。

我正在检查 FragmentStatePageAdapter 的代码,我看到了这个方法:

@Override
52     public Object More ...instantiateItem(View container, int position) {
53         // If we already have this item instantiated, there is nothing
54         // to do.  This can happen when we are restoring the entire pager
55         // from its saved state, where the fragment manager has already
56         // taken care of restoring the fragments we previously had instantiated.
57         if (mFragments.size() > position) {
58             Fragment f = mFragments.get(position);
59             if (f != null) {
60                 return f;
61             }
62         }
63 
64         if (mCurTransaction == null) {
65             mCurTransaction = mFragmentManager.beginTransaction();
66         }
67 
68         Fragment fragment = getItem(position);
69         if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
70         if (mSavedState.size() > position) {
71             Fragment.SavedState fss = mSavedState.get(position);
72             if (fss != null) {
73                 fragment.setInitialSavedState(fss);
74             }
75         }
76         while (mFragments.size() <= position) {
77             mFragments.add(null);
78         }
79         fragment.setMenuVisibility(false);
80         mFragments.set(position, fragment);
81         mCurTransaction.add(container.getId(), fragment);
82 
83         return fragment;
84     }

您可以使用此方法获取已为该特定位置设置的片段。所以如果你想让Fragment在第1位,你只需要打电话:

myFragmentStatePageAdpater.instantiateItem(null, 1)

希望有所帮助

答案 2 :(得分:5)

我通过向getItemTag(int position)添加FragmentStatePagerAdapter函数来实现此目的。如果有其他人想要使用此路线,则更新文件的插入版本为here

答案 3 :(得分:2)

如果重新创建活动,在getItem()方法中将片段存储到HashMap的解决方案不起作用!

问题是,FragmentStatePagerAdapter的默认实现直接从FragmentManager恢复片段而不调用getItem()方法。这意味着您永远不会有机会存储对片段的引用。

有关详细信息,请参阅源代码: FragmentStatePagerAdapter#restoreState

这基本上意味着获取片段引用的唯一方法是使用反射。如果您使用支持库,这是相对 * 安全 **。

以下是:

    @SuppressWarnings("unchecked")
    public Fragment getFragment(int position) {
        try {
            Field f = FragmentStatePagerAdapter.class.getDeclaredField("mFragments");
            f.setAccessible(true);
            ArrayList<Fragment> fragments = (ArrayList<Fragment>) f.get(this);
            if (fragments.size() > position) {
                return fragments.get(position);
            }
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

相对 *意味着它不像在框架类上使用反射那样危险。支持类被编译到您的应用程序中,此代码无法在您的设备上运行,但会在其他设备上崩溃。您仍然可以通过将支持库更新到更新版本来破坏此解决方案

安全 **使用反射永远不会安全;但是,当您使用的库存在设计缺陷时,您必须使用此方法。

答案 4 :(得分:1)

正如@Lancelot所提到的,你可以调用并将结果转换为你的SuperCoolFragment:

SuperCoolFragment frag = (SuperCoolFragment) yourFragmentStatePageAdpater.instantiateItem(null, position); 

答案 5 :(得分:0)

我尝试了Frank的答案,建议使用其中链接的教程中的第二种方法,机器人因为某种原因将方法getFragment()添加到我的ViewPagerAdapter后,我无法通过mViewPager.getAdapter访问它。 getFragment ()好像它不存在,所以我将mPageReferenceMap声明从ViewPagerAdapter移动到封闭类,因此可以从封闭类的任何一点(在我的例子中是MainActivity的其他片段的回调方法)轻松访问它,最终使可以在片段之间传递数据。 感谢弗兰克,如果你喜欢我,可以随意使用我的夹具来解决访问自定义getFragment方法的问题。

答案 6 :(得分:0)

另一种解决方案是复制粘贴FragmentStatePagerAdapter的源代码并添加getFragment方法:

public Fragment getFragment(int position) {
    if (position >= mFragments.size()) {
        return null;
    }
    return mFragments.get(position);
}

答案 7 :(得分:0)

覆盖setPrimaryItem,并使用该位置,它将是正确的页面索引:

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
        super.setPrimaryItem(container,position,object);
        if (position == 1) {
            currentPageIndex=cameraFragment.pageIndex;
}

答案 8 :(得分:-10)

创建一个持有者来保存新的SuperCoolFragment对象。然后标记并返回持有者对象。

@Override
public Fragment getItem(int position) {
    SuperCoolFragment superCoolFragment = new SuperCoolFragment();
    superCoolFragment.setTag(YOUR_TAG);
    return superCoolFragment;
}