web-dev-qa-db-fra.com

Obtenir l'instance actuelle de Fragment dans le viewpager

Ci-dessous est mon code qui a 3 Fragment classes incorporé dans chacun des 3 onglets de ViewPager. J'ai une option de menu. Comme indiqué dans onOptionsItemSelected(), en sélectionnant une option, je dois mettre à jour le fragment actuellement visible. Pour mettre à jour cela, je dois appeler une méthode qui est dans la classe fragment. Quelqu'un peut-il s'il vous plaît suggérer comment appeler cette méthode?

public class MainActivity  extends ActionBarActivity {

     ViewPager ViewPager;
     TabsAdapter TabsAdapter;

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

            ViewPager = new ViewPager(this);
            ViewPager.setId(R.id.pager);
            setContentView(ViewPager);

            final ActionBar bar = getSupportActionBar();

            bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

            //Attaching the Tabs to the fragment classes and setting the tab title.
            TabsAdapter = new TabsAdapter(this, ViewPager);
            TabsAdapter.addTab(bar.newTab().setText("FragmentClass1"),
                    FragmentClass1.class, null);
            TabsAdapter.addTab(bar.newTab().setText("FragmentClass2"),
              FragmentClass2.class, null);
            TabsAdapter.addTab(bar.newTab().setText("FragmentClass3"),
              FragmentClass3.class, null);


            if (savedInstanceState != null) {
                bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
            }

        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {

            switch (item.getItemId()) {

            case R.id.addText:

           **// Here I need to call the method which exists in the currently visible Fragment class**

                    return true;

            }

            return super.onOptionsItemSelected(item);
        }


     @Override
     protected void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
            outState.putInt("tab", getSupportActionBar().getSelectedNavigationIndex());

     }

     public static class TabsAdapter extends FragmentPagerAdapter
      implements ActionBar.TabListener, ViewPager.OnPageChangeListener {

      private final Context mContext;
            private final ActionBar mActionBar;
            private final ViewPager mViewPager;
            private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();

            static final class TabInfo {
                private final Class<?> clss;
                private final Bundle args;

                TabInfo(Class<?> _class, Bundle _args) {
                    clss = _class;
                    args = _args;
                }
            }

      public TabsAdapter(ActionBarActivity activity, ViewPager pager) {
       super(activity.getSupportFragmentManager());
                mContext = activity;
                mActionBar = activity.getSupportActionBar();
                mViewPager = pager;
                mViewPager.setAdapter(this);
                mViewPager.setOnPageChangeListener(this);
            }

      public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
                TabInfo info = new TabInfo(clss, args);
                tab.setTag(info);
                tab.setTabListener(this);
                mTabs.add(info);
                mActionBar.addTab(tab);
                notifyDataSetChanged();

            }

      @Override
      public void onPageScrollStateChanged(int state) {
       // TODO Auto-generated method stub

      }

      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
       // TODO Auto-generated method stub

      }

      @Override
      public void onPageSelected(int position) {
       // TODO Auto-generated method stub
       mActionBar.setSelectedNavigationItem(position);
      }

      @Override
      public void onTabReselected(Tab tab, FragmentTransaction ft) {
       // TODO Auto-generated method stub

      }

      @Override
      public void onTabSelected(Tab tab, FragmentTransaction ft) {
       Object tag = tab.getTag();
                for (int i=0; i<mTabs.size(); i++) {
                    if (mTabs.get(i) == tag) {
                        mViewPager.setCurrentItem(i);

                    }
                }

                tabPosition = tab.getPosition();
      }

      @Override
      public void onTabUnselected(Tab tab, FragmentTransaction ft) {
       // TODO Auto-generated method stub

      }

      @Override
      public Fragment getItem(int position) {
       TabInfo info = mTabs.get(position);
                return Fragment.instantiate(mContext, info.clss.getName(), info.args);
      }

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

     }

    }

Supposons ci-dessous la classe de fragment avec la méthode updateList() je veux appeler:

 public class FragmentClass1{

    ArrayList<String> originalData;


    @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
           Bundle savedInstanceState) {

          View fragmentView = inflater.inflate(R.layout.frag1, container, false);

          originalData = getOriginalDataFromDB();

          return fragmentView;

         }


    public void updateList(String text)
    {
       originalData.add(text);
       //Here I could do other UI part that need to added
    }
}
178
rick

en sélectionnant une option, je dois mettre à jour le fragment qui est actuellement visible.

Pour ce faire, utilisez une astuce liée à l'implémentation FragmentPagerAdapter:

case R.id.addText:
     Fragment page = getSupportFragmentManager().findFragmentByTag("Android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
     // based on the current position you can then cast the page to the correct 
     // class and call the method:
     if (ViewPager.getCurrentItem() == 0 && page != null) {
          ((FragmentClass1)page).updateList("new item");     
     } 
return true;

Veuillez repenser votre convention de dénomination des variables, en utilisant comme nom de variable le nom de la classe est très déroutant (donc pas ViewPager ViewPager, utilisez ViewPager mPager par exemple).

174
Luksprog
    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);
        }
    }
132
Mindphaser

Tout d’abord, gardez une trace de toutes les pages de fragments "actives". Dans ce cas, vous gardez une trace des pages de fragment dans FragmentStatePagerAdapter, utilisé par ViewPager.

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

Pour éviter de conserver une référence aux fragments "inactifs", vous devez implémenter la méthode destroyItem (...) de FragmentStatePagerAdapter:

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

et lorsque vous devez accéder à la page actuellement visible, vous appelez ensuite:

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

La méthode getFragment (int) de MyAdapter ressemble à ceci:

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

J'espère que ça peut aider!

106
FraZer

C'est la seule façon pour moi de ne pas obtenir NullPointerException pour les variables d'instance de ce fragment de classe. Cela pourrait être utile pour les autres qui ont collé à la même chose. Dans onOptionsItemSelected (), j'ai codé la manière suivante:

if(viewPager.getCurrentItem() == 0) {
    FragmentClass1 frag1 = (FragmentClass1)viewPager
                            .getAdapter()
                            .instantiateItem(viewPager, viewPager.getCurrentItem());
    frag1.updateList(text); 
} else if(viewPager.getCurrentItem() == 1) {
    FragmentClass2 frag2 = (FragRecentApps)viewPager
                            .getAdapter()
                            .instantiateItem(viewPager, viewPager.getCurrentItem());
    frag2.updateList(text);
}
86
rick

FragmentStatePagerAdapter a une méthode publique avec le nom instantiateItem qui renvoie votre fragment en fonction des valeurs de paramètre spécifiées. Cette méthode a deux paramètres ViewGroup (ViewPager) et position.

public Object instantiateItem(ViewGroup container, int position);

Utilisé cette méthode pour obtenir le fragment de la position spécifiée,

Fragment fragment = (Fragment) adaper.instantiateItem(mViewPager, position);
28
Krunal Shah

Je sais que c'est trop tard, mais j'ai des moyens très simples de le faire,

// pour fragment à 0 possition

((mFragment) viewPager.getAdapter().instantiateItem(viewPager, 0)).yourMethod();
10
Pratik Mhatre

Il y a beaucoup de réponses ici qui n'abordent pas vraiment le fait fondamental qu'il n'y a vraiment pas de moyen de faire cela de manière prévisible, et d'une manière qui ne vous empêche pas de vous tirer dans le pied à un moment donné.

FragmentStatePagerAdapter est la seule classe qui sait comment accéder de manière fiable aux fragments suivis par FragmentManager - toute tentative d'essayer de deviner l'identifiant ou la balise du fragment n'est pas fiable, à long terme. Et les tentatives de suivi manuel des instances ne fonctionneront probablement pas bien lorsque l'état sera sauvegardé/restauré, car FragmentStatePagerAdapter risque de ne pas appeler les rappels lors de la restauration de l'état.

La seule chose que j’ai pu faire fonctionner est de copier le code pour FragmentStatePagerAdapter et d’ajouter une méthode qui renvoie le fragment, à partir d’une position (mFragments.get(pos)). Notez que cette méthode suppose que le fragment est réellement disponible (c’est-à-dire qu’il était visible à un moment donné).

Si vous êtes particulièrement aventureux, vous pouvez utiliser la réflexion pour accéder aux éléments de la liste privée mFragments, mais nous revenons à la case départ (il n'est pas garanti que le nom de la liste reste identique).

9
Melllvar

Fragment actuel:

Cela fonctionne si vous avez créé un projet avec le modèle de barre d’onglet fragments.

Fragment f = mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());

Notez que cela fonctionne avec l'implémentation du modèle d'activité à onglets par défaut.

5
hasan
getSupportFragmentManager().getFragments().get(viewPager.getCurrentItem());

Convertissez l'instance extraite de la ligne ci-dessus en fragment avec lequel vous souhaitez travailler Fonctionne parfaitement bien.

viewPager

est l'instance de pager qui gère les fragments.

4
Gaurav Pangam

La meilleure façon de le faire est d'appeler CallingFragmentName fragment = (CallingFragmentName) viewPager .getAdapter () .instantiateItem (viewPager, viewPager.getCurrentItem ()); Il ré-instanciera votre fragment appelant, de sorte qu'il ne lève pas d'exception de pointeur null et n'appelle aucune méthode de ce fragment.

4
Ritesh Adulkar

J'ai utilisé les éléments suivants:

 int index = vpPager.getCurrentItem();
 MyPagerAdapter adapter = ((MyPagerAdapter)vpPager.getAdapter());
 MyFragment suraVersesFragment = (MyFragment)adapter.getRegisteredFragment(index);
3
real 19

FragmentStatePagerAdapter a une variable d'instance privée appelée mCurrentPrimaryItem de type Fragment. On peut seulement se demander pourquoi les développeurs Android ne l’ont pas fourni avec un getter. Cette variable est instanciée dans la méthode setPrimaryItem(). Donc, substituez cette méthode de manière à obtenir la référence à cette variable. J'ai simplement fini par déclarer ma propre mCurrentPrimaryItem et copier le contenu de setPrimaryItem() dans ma substitution.

Dans votre implémentation de FragmentStatePagerAdapter:

private Fragment mCurrentPrimaryItem = null;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment)object;
    if (fragment != mCurrentPrimaryItem) {
        if (mCurrentPrimaryItem != null) {
            mCurrentPrimaryItem.setMenuVisibility(false);
            mCurrentPrimaryItem.setUserVisibleHint(false);
        }
        if (fragment != null) {
            fragment.setMenuVisibility(true);
            fragment.setUserVisibleHint(true);
        }
        mCurrentPrimaryItem = fragment;
    }
}

public TasksListFragment getCurrentFragment() {
    return (YourFragment) mCurrentPrimaryItem;
}
2
Sevastyan

Lorsque nous utilisons viewPager, un bon moyen d'accéder à l'instance de fragment en activité est instantiateItem (viewpager, index). // index-index du fragment dont vous voulez une instance.

par exemple, j'accède à l'instance de fragment de 1 index- 

Fragment fragment = (Fragment) viewPageradapter.instantiateItem(viewPager, 1);

if (fragment != null && fragment instanceof MyFragment) {      
 ((MyFragment) fragment).callYourFunction();

}
2
chahat jain

J'ai eu le même problème et l'ai résolu en utilisant ce code.

MyFragment fragment = (MyFragment) thisActivity.getFragmentManager().findFragmentById(R.id.container);

Il suffit de remplacer le nom MyFragment par le nom de votre fragment et d'ajouter l'id de votre conteneur de fragments.

1
Bilal Soomro

Vous pouvez définir la PagerAdapter comme ceci, alors vous pourrez obtenir n'importe quelle Fragment dans ViewPager.

private class PagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

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

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

    public void addFragment(Fragment fragment) {
        mFragmentList.add(fragment);
    }
}

Pour obtenir la Fragment actuelle

Fragment currentFragment = mPagerAdapter.getItem(mViewPager.getCurrentItem());
1
Linh

Le scénario en question est mieux servi par chaque fragment qui ajoute ses propres éléments de menu et gère directement onOptionsItemSelected (), comme décrit dans documentation officielle . Il vaut mieux éviter les astuces non documentées.

1
Y2i

Basé sur ce qu'il a répondu @chahat jain:

"Lorsque nous utilisons viewPager, un bon moyen d'accéder à l'instance de fragment en activité est instantiateItem (viewpager, index). // index-index du fragment dont vous voulez une instance."

Si vous voulez faire cela dans kotlin  

val fragment =  mv_viewpager.adapter!!.instantiateItem(mv_viewpager, 0) as Fragment
                if ( fragment is YourFragmentFragment)
                 {
                    //DO somthign 
                 }

0 à l'instance de fragment de 0

// ============================================= ======================== // // ################## ########### Exemple d'utilisation ################################ ////=========================================== =========================== //

Voici un exemple complet pour avoir une vision la plus perdante de 

voici mon veiewPager dans le fichier .xml

   ...
    <Android.support.v4.view.ViewPager
                Android:id="@+id/mv_viewpager"
                Android:layout_width="match_parent"
                Android:layout_height="match_parent"
                Android:layout_margin="5dp"/>
    ...

Et l'activité à la maison où j'insère l'onglet 

...
import kotlinx.Android.synthetic.main.movie_tab.*

class HomeActivity : AppCompatActivity() {

    lateinit var  adapter:HomeTabPagerAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
       ...
    }



    override fun onCreateOptionsMenu(menu: Menu) :Boolean{
            ...

        mSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
          ...

            override fun onQueryTextChange(newText: String): Boolean {

                if (mv_viewpager.currentItem  ==0)
                {
                    val fragment =  mv_viewpager.adapter!!.instantiateItem(mv_viewpager, 0) as Fragment
                    if ( fragment is ListMoviesFragment)
                        fragment.onQueryTextChange(newText)
                }
                else
                {
                    val fragment =  mv_viewpager.adapter!!.instantiateItem(mv_viewpager, 1) as Fragment
                    if ( fragment is ListShowFragment)
                        fragment.onQueryTextChange(newText)
                }
                return true
            }
        })
        return super.onCreateOptionsMenu(menu)
    }
        ...

}
1
DINA TAKLIT

Après avoir lu tous les commentaires et réponses, je vais expliquer une solution optimale à ce problème. La meilleure option est la solution de @ rik, mon amélioration est donc basée sur la sienne. 

Au lieu d'avoir à demander à chaque FragmentClass comme

if(FragmentClass1){
   ...
if(FragmentClass2){
   ...
}

Créez votre propre interface et créez vos fragments enfants pour les implémenter, quelque chose comme

public interface MyChildFragment {
    void updateView(int position);
}

Ensuite, vous pouvez faire quelque chose comme ceci pour initier et mettre à jour vos fragments internes.

Fragment childFragment = (Fragment) mViewPagerDetailsAdapter.instantiateItem(mViewPager,mViewPager.getCurrentItem());

if (childFragment != null) {
   ((MyChildFragment) childFragment).updateView();
}

P.S. Faites attention à l'endroit où vous mettez ce code, si vous appelez insatiateItem avant que le système ne le crée réellement, la variable savedInstanceState de votre fragment enfant sera nulle.

public void onCreate(@Nullable Bundle savedInstanceState){
      super(savedInstanceState)
}

Va planter votre application.

Bonne chance

1
Pedro Varela

C'est plus futuriste que la réponse acceptée:

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    /* ------------------------------------------------------------------------------------------ */
    // region Private attributes :

    private Context _context;
    private FragmentManager _fragmentManager;
    private Map<Integer, String> _fragmentsTags = new HashMap<>();

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Constructor :

    public MyFragmentPagerAdapter(Context context, FragmentManager fragmentManager) {

        super(fragmentManager);

        _context = context;
        _fragmentManager = fragmentManager;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region FragmentPagerAdapter methods :

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

    @Override
    public Fragment getItem(int position) {

        if(_fragmentsTags.containsKey(position)) {

            return _fragmentManager.findFragmentByTag(_fragmentsTags.get(position));
        }
        else {

            switch (position) {

                case 0 : { return Fragment.instantiate(_context, Tab1Fragment.class.getName()); }
                case 1 : { return Fragment.instantiate(_context, Tab2Fragment.class.getName()); }
            }
        }

        return null;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {

        // Instantiate the fragment and get its tag :
        Fragment result = (Fragment) super.instantiateItem(container, position);
        _fragmentsTags.put(position, result.getTag());

        return result;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */
}
1
Tim Autin

Vous pouvez implémenter un BroadcastReceiver dans le fragment et envoyer un message d'intention à partir de n'importe où. Le destinataire du fragment peut écouter L'action spécifique et invoquer la méthode de l'instance.

Un inconvénient est de s'assurer que le composant View est déjà instancié Et (et pour certaines opérations, telles que le défilement d'une liste, ListView Doit déjà être rendu).

0
nichole

Remplacez setPrimaryItem de votre FragmentPagerAdapter: l'objet est le fragment visible:

 @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (mCurrentFragment != object) {
                mCurrentFragment = (LeggiCapitoloFragment) object;
            }
            super.setPrimaryItem(container, position, object);
        }
0
Flavio Barisi

Dans mon activité, j'ai:

int currentPage = 0;//start at the first tab
private SparseArray<Fragment> fragments;//list off fragments
viewPager.setOnPageChangeListener(new OnPageChangeListener() {

@Override
public void onPageSelected(int pos) {
        currentPage = pos;//update current page
}

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



@Override
public void onAttachFragment(Fragment fragment) {
    super.onAttachFragment(fragment);
    if(fragment instanceof Fragment1)
        fragments.put(0, fragment);
    if(fragment instanceof Fragment2)
        fragments.put(2, fragment);
    if(fragment instanceof Fragment3)
        fragments.put(3, fragment);
    if(fragment instanceof Fragment4)
        fragments.put(4, fragment);
}

Then I have the following method for getting the current fragment

public Fragment getCurrentFragment() {
    return fragments.get(currentPage);
}
0
J. Musyoka

Pour obtenir le fragment actuel, positionnez-vous dans ViewPager sur public void onPageSelected (position finale) , puis

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

SECTNUM - argument de position affecté dans public statique PlaceholderFragment newInstance (int sectionNumber); de fragment

getChildFragmentManager () ou getFragmentManager () - dépend de la création de SectionsPagerAdapter

0
Evgeny
final ViewPager.OnPageChangeListener onPageChangeListener =
    new ViewPager.OnPageChangeListener() {
      @Override public void onPageSelected(int position) {
        switch (position) {
          case 0:
            FragmentClass1 frag1 =
                (FragmentClass1) viewPager.getAdapter()
                    .instantiateItem(viewPager, viewPager.getCurrentItem());
            frag1.updateList("Custom text");
            break;
          case 1:
            break;
          case 2:
            break;
        }
      }

      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

      }

      @Override public void onPageScrollStateChanged(int state) {
      }
    };


 viewPager.addOnPageChangeListener(onPageChangeListener);

viewPager.setAdapter(adapter);


viewPager.post(new Runnable() {
  @Override public void run() {
    onPageChangeListener.onPageSelected(viewPager.getCurrentItem());
  }
});

Parfois, le fragment peut envoyer un objet null, vous pouvez donc exécuter un nouveau Runnable pour résoudre le problème lors du premier chargement.

Merci rick

0
Benjamín Rengifo

Si votre téléavertisseur est à l'intérieur d'un fragment, utilisez ceci:

private fun getPagerCurrentFragment(): Fragment? {
    return childFragmentManager.findFragmentByTag("Android:switcher:${R.id.myViewPagerId}:${myViewPager.currentItem}")
}

R.id.myViewPagerId est l'id de votre ViewPager dans la mise en page xml.

0
vovahost

Procurez-vous simplement l’élément en cours à partir du pageur, puis demandez à votre adaptateur le fragment correspondant à cette position.

 int currentItem = viewPager.getCurrentItem();
    Fragment item = mPagerAdapter.getItem(currentItem);
    if (null != item && item.isVisible()) {
       //do whatever want to do with fragment after doing type checking
        return;
    }
0
Pankaj kumar

C'est le hack le plus simple:

fun getCurrentFragment(): Fragment? {
    return if (count == 0) null
    else instantiateItem(view_pager, view_pager.currentItem) as? Fragment
}

(code kotlin)

Il suffit d’appeler instantiateItem(viewPager, viewPager.getCurrentItem() et de le convertir en Fragment. Votre article serait déjà instancié. Pour être sûr, vous pouvez ajouter un chèque pour getCount.

Fonctionne avec FragmentPagerAdapter et FragmentStatePagerAdapter!

0
vedant