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
}
}
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).
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);
}
}
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!
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);
}
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);
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();
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).
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.
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.
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.
J'ai utilisé les éléments suivants:
int index = vpPager.getCurrentItem();
MyPagerAdapter adapter = ((MyPagerAdapter)vpPager.getAdapter());
MyFragment suraVersesFragment = (MyFragment)adapter.getRegisteredFragment(index);
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;
}
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();
}
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.
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());
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.
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)
}
...
}
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
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
/* ------------------------------------------------------------------------------------------ */
}
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).
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);
}
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);
}
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
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
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}")
}
Où R.id.myViewPagerId
est l'id de votre ViewPager dans la mise en page xml.
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;
}
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
!