Retrieve a Fragment from a ViewPager

前端 未结 23 2512
逝去的感伤
逝去的感伤 2020-11-21 11:13

I\'m using a ViewPager together with a FragmentStatePagerAdapter to host three different fragments:

  • [Fragment1]
  • [Fragment2]<
相关标签:
23条回答
  • 2020-11-21 11:35

    This is based on Steven's answer above. This will return actual instance of the fragment which is already attached to the parent activity.

    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
                    }
                }
            }
        }
    
    0 讨论(0)
  • 2020-11-21 11:36

    For grabbing fragments out of a ViewPager there are a lot of answers on here and on other related SO threads / blogs. Everyone I have seen is broken, and they generally seem to fall into one of the two types listed below. There are some other valid solutions if you only want to grab the current fragment, like this other answer on this thread.

    If using FragmentPagerAdapter see below. If using FragmentStatePagerAdapter its worth looking at this. Grabbing indexes that are not the current one in a FragmentStateAdapter is not as useful as by the nature of it these will be completely torn down went out of view / out of offScreenLimit bounds.

    THE UNHAPPY PATHS

    Wrong: Maintain your own internal list of fragments, added to when FragmentPagerAdapter.getItem() is called

    • Usually using a SparseArray or Map
    • Not one of the many examples I have seen accounts for lifecycle events so this solution is fragile. As getItem is only called the first time a page is scrolled to (or obtained if your ViewPager.setOffscreenPageLimit(x) > 0) in the ViewPager, if the hosting Activity / Fragment is killed or restarted then the internal SpaseArray will be wiped out when the custom FragmentPagerActivity is recreated, but behind the scenes the ViewPagers internal fragments will be recreated, and getItem will NOT be called for any of the indexes, so the ability to get a fragment from index will be lost forever. You can account for this by saving out and restoring these fragment references via FragmentManager.getFragment() and putFragment but this starts to get messy IMHO.

    Wrong: Construct your own tag id matching what is used under the hood in FragmentPagerAdapter and use this to retrieve the page Fragments from the FragmentManager

    • This is better insomuch as it copes with the losing-fragment-references problem in the first internal-array solution, but as rightly pointed out in the answers above and elsewhere on the net - it feels hacky as its a private method internal to ViewPager that could change at any time or for any OS version.

    The method thats recreated for this solution is

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

    A HAPPY PATH: ViewPager.instantiateItem()

    A similar approach to getItem() above but non-lifecycle-breaking is to this is to hook into instantiateItem() instead of getItem() as the former will be called everytime that index is created / accessed. See this answer

    A HAPPY PATH: Construct your own FragmentViewPager

    Construct your own FragmentViewPager class from the source of the latest support lib and change the method used internally to generate the fragment tags. You can replace it with the below. This has the advantage that you know the tag creation will never change and your not relying on a private api / method, which is always dangerous.

    /**
     * @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;
    }
    

    Then as the doc says, when you want to grab a fragment used for an index just call something like this method (which you can put in the custom FragmentPagerAdapter or a subclass) being aware the result may be null if getItem has not yet been called for that page i.e. its not been created yet.

    /**
     * @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;
    }
    

    This is a simple solution and solves the issues in the other two solutions found everywhere on the web

    0 讨论(0)
  • 2020-11-21 11:37

    Ok for the adapter FragmentStatePagerAdapter I fund a solution :

    in your 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);
    

    and create a methode in your class FragmentActivity - So that method give you access to your Fragment, you just need to give it the position of the fragment you want:

    public Fragment getActiveFragment(int position) {
    String name = MyPagerAdapter.makeFragmentName(position);
    return getSupportFragmentManager().findFragmentByTag(name);
    }
    

    in your Adapter :

    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;
    }
    
    }
    
    0 讨论(0)
  • 2020-11-21 11:38

    Add next methods to your 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) has to work.

    Here is the solution implemented into ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d. If something fail you will see good crash log.

    0 讨论(0)
  • 2020-11-21 11:39

    FragmentPagerAdapter is the factory of the fragments. To find a fragment based on its position if still in memory use this:

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

    Sample code for v4 support api.

    0 讨论(0)
  • 2020-11-21 11:41

    For my case, none of the above solutions worked.

    However since I am using the Child Fragment Manager in a Fragment, the following was used:

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

    This will only work if your fragments in the Manager correspond to the viewpager item.

    0 讨论(0)
提交回复
热议问题