Android FragmentStatePagerAdapter,如何标记片段以便稍后查找

Android FragmentStatePagerAdapter,如何标记片段以便稍后查找,android,android-viewpager,Android,Android Viewpager,使用FragmentStatePageAdapter时,我得到如下片段: @Override public Fragment getItem(int position) { return new SuperCoolFragment(); } public MyFragment getFragment(int key) { return mPageReferenceMap.get(key); } 然而,稍后在我的代码中,我需要找到这个片段来设置属

使用
FragmentStatePageAdapter
时,我得到如下片段:

    @Override
    public Fragment getItem(int position) {
        return new SuperCoolFragment();
    }
public MyFragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}
然而,稍后在我的代码中,我需要找到这个片段来设置属性。在我使用片段的应用程序的一些其他部分中,我基本上会使用
findFragmentByTag(tag)
标记它们并查找它们,但现在我不知道如何做


如何使用
FragmentStatePageAdapter
查找片段?

创建一个固定器来固定新的SupercolFragment对象。然后标记并返回holder对象

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

*编辑*

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

Fragment myFragment = (Fragment) adapter.instantiateItem(viewPager, viewPager.getCurrentItem());
*旧解决方案*

最佳选择是第二种解决方案:

简而言之:您可以跟踪所有“活动”片段页面。在本例中,您将跟踪FragmentStatePagerAdapter中的片段页面,该页面由ViewPager使用

public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferenceMap.put(index, myFragment);
    return myFragment;
}
为了避免保留对“非活动”片段页面的引用,我们需要实现FragmentStatePagerAdapter的destroyItem(…)方法:

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

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);
。。。其中MyAdapter的getFragment(int)方法如下所示:

    @Override
    public Fragment getItem(int position) {
        return new SuperCoolFragment();
    }
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;
}

我通过向
FragmentStatePagerAdapter
添加一个
getItemTag(int位置)
函数来实现这一点。如果其他人想使用此方法,则可以使用更新文件的插入版本。

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

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

有关更多信息,请参阅源代码:

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

以下是方法:

    @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);
        }
    }
@SuppressWarnings(“未选中”)
公共片段getFragment(int位置){
试一试{
字段f=FragmentStatePagerAdapter.class.getDeclaredField(“MFFragments”);
f、 setAccessible(true);
ArrayList片段=(ArrayList)f.get(this);
if(fragments.size()>位置){
返回碎片。获取(位置);
}
返回null;
}捕获(例外e){
抛出新的运行时异常(e);
}
}
相对而言*意味着它不像在框架类上使用反射那样危险。支持类被编译到你的应用程序中,这段代码不可能在你的设备上工作,但会在其他设备上崩溃。通过将支持库更新到新版本,您仍有可能破坏此解决方案


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

我找到了另一种方法,不确定使用它是否会有任何问题,我觉得这样做没问题

我正在检查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     }

希望有帮助

我尝试了Frank的答案,建议使用链接教程中的第二种方法,在将方法getFragment()添加到我的ViewPagerAdapter后,出于某种原因,我无法通过mViewPager.getAdapter访问它。getFragment(),好像它不存在一样,因此,我将mPageReferenceMap声明从ViewPagerAdapter移到了封闭类中,这样就可以从封闭类的任何一点轻松访问它(在我的例子中,它是MainActivity从其他片段的回调方法),这最终使在片段之间传递数据成为可能。
感谢Frank,如果您喜欢我在访问自定义getFragment方法时遇到问题,请随意实现我的fixture

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

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

正如@Lancelot所提到的,您可以调用并将结果投射到SupercolFragment:

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

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

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

使用final FragmentManager fm=getActivity().getSupportFragmentManager()怎么样;最终Bundle args=新Bundle();fm.putFragment(args,TAG,this)并将此args包发送到新片段的新实例?我想知道如果进程被终止并重新启动,对片段的引用会发生什么。。。持久化args包中的引用仍然指向片段的正确实例吗?我最终使用LocalBroadcastManager来传递片段。你不应该简单地调用你的Adapter.getItem(position)吗?我已经讨论了所有这些以及其他一些答案,但是@2cupsOfTech,你的注释,是我的答案!打得好,链接中的第二个解决方案正是我需要的FragmentStatePagerAdapter。我刚刚开始熟悉ViewPager API,对如何跟踪感到困惑。这是最直截了当的思考方式。我也使用过类似的解决方案,希望链接有我以前没有做过的事情:(无论如何,这还不错,我只希望适配器能够提供一种机制来访问它内部使用的FragmentManager。如果主机活动被销毁并从头开始重新创建,这将不起作用。getItem(index)将不会被调用,您的mPageReferenceMap将为空。转到开发人员选项->“不要保留活动”你自己看看。我检查了我的代码,很早以前就退出了,但我认为添加了实例化项,请参见上面的编辑。也许这会解决你的问题?同意@Alexe吗