从ViewPager中检索片段

时间:2012-01-09 07:38:38

标签: android android-fragments android-viewpager

我正在使用ViewPagerFragmentStatePagerAdapter来托管三个不同的片段:

  • [片段1]
  • [Fragment2]
  • [Fragment3]

当我想从Fragment1的{​​{1}}获取ViewPager时。

问题是什么,我该如何解决?

23 个答案:

答案 0 :(得分:495)

主要答案依赖于框架生成的名称。如果这种情况发生了变化,那么它将不再起作用。

此解决方案如何,覆盖instantiateItem()的{​​{1}}和destroyItem()

Fragment(State)PagerAdapter

在处理可用的碎片时,这似乎对我有用。尚未实例化的片段在调用public class MyPagerAdapter extends FragmentStatePagerAdapter { SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>(); public MyPagerAdapter(FragmentManager fm) { super(fm); } @Override public int getCount() { return ...; } @Override public Fragment getItem(int position) { return MyFragment.newInstance(...); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment fragment = (Fragment) super.instantiateItem(container, position); registeredFragments.put(position, fragment); return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { registeredFragments.remove(position); super.destroyItem(container, position, object); } public Fragment getRegisteredFragment(int position) { return registeredFragments.get(position); } } 时将返回null。但我一直在使用此功能主要是从getRegisteredFragment Fragment获取当前ViewPager,这不会返回adapater.getRegisteredFragment(viewPager.getCurrentItem())

我不知道此解决方案的任何其他缺点。如果有的话,我想知道。

答案 1 :(得分:85)

为了从ViewPager中抓取片段,这里和其他相关的SO线程/博客上有很多答案。我见过的每个人都被打破了,他们通常似乎属于下面列出的两种类型之一。如果您只想获取当前片段,还有一些其他有效的解决方案,例如this此线程上的其他答案。

如果使用 FragmentPagerAdapter ,请参阅下文。如果使用 FragmentStatePagerAdapter 值得查看this。在FragmentStateAdapter中抓取不是当前索引的索引并不像它的性质那样有用,这些索引将完全拆除掉在offScreenLimit边界之外。

UNHAPPY PATHS

错误:维护您自己的内部片段列表,在调用FragmentPagerAdapter.getItem()时添加

  • 通常使用SparseArrayMap
  • 我见过很多关于生命周期事件的例子,所以这个解决方案很脆弱。由于getItem仅在ViewPager.setOffscreenPageLimit(x)中滚动到(或获得ViewPager&gt; 0时)页面时调用Activity,如果托管Fragment / {{ 1}}被杀死或重新启动,然后在重新创建自定义FragmentPagerActivity时将清除内部SpaseArray,但在幕后将重新创建ViewPagers内部片段,getItem NOT < / strong>为任何索引调用,因此从索引中获取片段的能力将永远丢失。您可以通过FragmentManager.getFragment()putFragment保存并恢复这些片段引用来解决这个问题,但这开始变得混乱恕我直言。

错误:根据FragmentPagerAdapter 中隐藏的内容构建您自己的代码ID,并使用此代码从FragmentManager

中检索页面片段
  • 这是更好的,因为它应对第一个内部阵列解决方案中的丢失片段引用问题,但正如在上面的答案和网络上的其他地方正确指出的那样 - 它感觉hacky因为它的 ViewPager内部的私有方法,可随时更改或任何操作系统版本。

为此解决方案重新创建的方法是

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

快乐路径:ViewPager.instantiateItem()

getItem()以上但非生命周期中断的类似方法是挂钩instantiateItem()而不是getItem(),因为前者将是每次创建/访问索引时调用。见this answer

快乐路径:构建您自己的FragmentViewPager

source of the latest support lib构建您自己的FragmentViewPager类,并更改内部使用的方法以生成片段标记。您可以使用以下内容替换它。这样做的好处是,您知道标签创建永远不会改变,而且您不依赖于私有API /方法,这总是很危险。

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "android:switcher:" + containerViewId + ":" + id;
}

然后正如文档所说,当你想要获取一个用于索引的片段时,只需调用类似这个方法的东西(可以放入自定义FragmentPagerAdapter或子类中)知道结果如果尚未为该页面调用getItem,则可能为null ,即尚未创建它。

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

这是一个简单的解决方案,可以解决网络上随处可见的其他两种解决方案中的问题

答案 2 :(得分:70)

将以下方法添加到FragmentPagerAdapter:

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

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

getActiveFragment(0)必须工作。

以下是ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d中实施的解决方案。如果出现故障,您将看到良好的崩溃日志。

答案 3 :(得分:38)

另一个简单的解决方案:

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }

答案 4 :(得分:21)

我知道这有几个答案,但也许这会对某人有所帮助。当我需要从Fragment获得ViewPager时,我使用了一个相对简单的解决方案。在ActivityFragment持有ViewPager的情况下,您可以使用此代码循环显示它所持有的每个Fragment

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • 如果您知道FragmentViewPager的位置,则可以致电getItem(knownPosition)

  • 如果您不知道FragmentViewPager的位置,您可以让您的孩子Fragments使用getUniqueId()等方法实现界面},用它来区分它们。或者,您可以遍历所有Fragments并检查班级类型,例如if(viewPagerFragment instanceof FragmentClassYouWant)

<强> !!!编辑!!!

我发现getItem只有当FragmentPagerAdapter需要创建第一次时才被Fragment调用,之后,它会出现使用Fragments回收FragmentManager。这样,FragmentPagerAdapter的许多实现都会在Fragment中创建 getItem。使用我的上述方法,这意味着每次调用Fragment时我们都会创建新的getItem,因为我们会审核FragmentPagerAdapter中的所有项目。因此,我找到了一种更好的方法,使用FragmentManager代替每个Fragment(使用已接受的答案)。这是一个更完整的解决方案,对我来说一直很好。

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

你需要这种方法。

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

答案 5 :(得分:10)

就我而言,上述解决方案均无效。

但是,由于我在片段中使用子片段管理器,因此使用了以下内容:

Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());

仅当Manager中的片段对应于viewpager项目时才会起作用。

答案 6 :(得分:6)

  

从ViewPager获取当前可见片段。我正在使用这个简单的陈述,它工作正常。

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }

答案 7 :(得分:3)

这是基于Steven的上述答案。这将返回已附加到父活动的片段的实际实例。

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }

答案 8 :(得分:2)

嘿,我已经回答了这个问题here。基本上,您需要覆盖

  

公共对象InstantiateItem(ViewGroup容器,int位置)

FragmentStatePagerAdapter的方法。

答案 9 :(得分:2)

我找不到一个简单,干净的方法来做到这一点。但是,ViewPager小部件只是另一个ViewGroup,它托管您的片段。 ViewPager将这些片段作为直接子节点。所以你可以迭代它们(使用.getChildCount()和.getChildAt()),看看你正在寻找的片段实例当前是否已加载到ViewPager并获得对它的引用。例如。您可以使用一些静态唯一ID字段来区分碎片。

请注意,ViewPager可能尚未加载您正在寻找的片段,因为它是像ListView这样的虚拟化容器。

答案 10 :(得分:2)

必须将FragmentPagerAdapter扩展到ViewPager适配器类中 如果您使用FragmentStatePagerAdapter,那么您将无法通过Fragment

找到ID
public static String makeFragmentName(int viewPagerId, int index) {
  return "android:switcher:" + viewPagerId + ":" + index;
}

如何使用此方法: -

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;

答案 11 :(得分:2)

需要在稍后阶段调用getItem()或其他方法,以获取FragmentViewPager内托管的引用。如果您想更新Fragment中的某些数据,请使用以下方法:Update ViewPager dynamically?

关键是在Adaper内设置新数据并致电notifyDataSetChanged(),然后拨打getItemPosition(),并为您提供Fragment的参考,并为您提供机会更新它。所有其他方式都要求您继续引用自己或其他一些不太好的解决方案。

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

答案 12 :(得分:2)

我首先制作了一份我将要使用的所有片段(List<Fragment> fragments;)的列表,然后将它们添加到寻呼机,以便更容易处理当前查看的片段。

所以:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

所以这可以叫做:

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

所以我可以把它放在if语句中,只有在正确的片段

上才会运行
Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

但这只是我的黑客和斜线方法,但它对我有用,我使用它会在按下后退按钮时对我当前显示的片段进行相关更改。

答案 13 :(得分:2)

FragmentPagerAdapter是片段的工厂。要在仍然在内存中时根据其位置查找片段,请使用:

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

v4支持api的示例代码。

答案 14 :(得分:1)

最佳解决方案是使用我们在名为CodePathSmartFragmentStatePagerAdapter处创建的扩展程序。在该指南之后,这使得从ViewPager检索片段和当前选择的片段变得更加容易。它还可以更好地管理嵌入在适配器中的片段的内存。

答案 15 :(得分:0)

最简单,最简洁的方式。如果ViewPager中的所有片段属于不同的类别,您可以检索并区分它们如下:

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}

答案 16 :(得分:0)

我用一种不同的方法实现了这一点。

我的自定义FragmentAdapter.getItem方法返回的不是新的MyFragment(),而是在FragmentAdapter构造函数中创建的MyFragment实例。

在我的活动中,我从适配器获取片段,检查它是否是instanceOf需要片段,然后转换并使用所需的方法。

答案 17 :(得分:0)

在/values/integers.xml

中创建整数资源ID
<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

然后在PagerAdapter getItem函数中:

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

然后在活动中编写此函数以获取片段引用:

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

通过调用上面的函数获取片段引用,然后将其转换为自定义片段:

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }

答案 18 :(得分:0)

在片段管理器中迭代片段的简便方法。查找具有节位置参数的viewpager,放置在 public static PlaceholderFragment newInstance(int sectionNumber)

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}

答案 19 :(得分:0)

在片段中

public int getArgument(){
   return mPage;
{
public void update(){

}

在FragmentActivity中

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}

答案 20 :(得分:0)

TabLayout中的

有片段的多个标签。你可以使用片段的索引通过Tag找到片段。

对于前。 Fragment1的索引是0,所以在findFragmentByTag()方法中,传递Viewpager的标签。使用fragmentTransaction你可以添加,替换片段。

String tag = "android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);

答案 21 :(得分:-1)

Fragment yourFragment = yourviewpageradapter.getItem(int index);

index是适配器中片段的位置,就像您先添加fragment1一样,以便fragment1index传递为0,依此类推,以便休息

答案 22 :(得分:-1)

适用于适配器FragmentStatePagerAdapter我为解决方案提供资金:

你的FragmentActivity中的

ActionBar mActionBar = getSupportActionBar(); 
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));

viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);

并在你的类FragmentActivity中创建一个方法 - 所以这个方法可以让你访问你的片段,你只需要给它你想要的片段的位置:

public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}
适配器中的

public class MyPagerAdapter extends FragmentStatePagerAdapter {

private final ActionBar actionBar;
private final FragmentManager fragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}

@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}

@Override
public int getCount() {
return this.actionBar.getTabCount();
}

@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}

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

}