android - studio - Obteniendo la instancia actual de Fragment en el viewpager
slide android studio (25)
A continuación está mi código que tiene 3 Fragment classes
cada una incrustada con cada una de las 3 pestañas en ViewPager
. Tengo una opción de menú. Como se muestra en onOptionsItemSelected()
, al seleccionar una opción, necesito actualizar el fragmento que está actualmente visible. Para actualizar tengo que llamar a un método que está en la clase de fragmento. ¿Puede alguien sugerir cómo llamar a ese método?
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();
}
}
}
Supongamos a continuación la clase de fragmento con el método updateList()
Quiero llamar:
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
}
}
al seleccionar una opción, necesito actualizar el fragmento que está actualmente visible.
Para obtener una referencia al fragmento actualmente visible, suponga que tiene una referencia a ViewPager
como mPager
. Luego los siguientes pasos obtendrán una referencia a currentFragment
:
-
PageAdapter adapter = mPager.getAdapter();
-
int fragmentIndex = mPager.getCurrentItem();
-
FragmentStatePagerAdapter fspa = (FragmentStatePagerAdapter)adapter;
-
Fragment currentFragment = fspa.getItem(fragmentIndex);
El único elenco utilizado en la línea 3 es válido por lo general. FragmentStatePagerAdapter
es un adaptador útil para ViewPager.
al seleccionar una opción, necesito actualizar el fragmento que está actualmente visible.
Una forma simple de hacerlo es usar un truco relacionado con la implementación de 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;
Por favor, reconsidere su convención de nomenclatura variable, usando como nombre de la variable el nombre de la clase es muy confuso (por lo que no ViewPager ViewPager
, use ViewPager mPager
por ejemplo).
Anule setPrimaryItem de su FragmentPagerAdapter: el objeto es el fragmento visible:
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (mCurrentFragment != object) {
mCurrentFragment = (LeggiCapitoloFragment) object;
}
super.setPrimaryItem(container, position, object);
}
Cuando usamos viewPager, una buena forma de acceder a la instancia de fragmento en la actividad es instantiateItem (viewpager, index). // index-index of fragment del cual quieres instancia.
por ejemplo, estoy accediendo a la instancia de fragmento de 1 índice-
Fragmento fragmento = (Fragmento) viewPageradapter.instantiateItem (viewPager, 1);
if (fragmento! = nulo && instancia de fragmento de MyFragment) {
((MyFragment) fragmento) .callYourFunction ();
}
Después de leer todos los comentarios y respuestas, voy a explicar una solución óptima para este problema. La mejor opción es la solución de @ rik, por lo que mi mejora se basa en la suya.
En lugar de tener que preguntar a cada FragmentClass como
if(FragmentClass1){
...
if(FragmentClass2){
...
}
Cree su propia interfaz y haga que los fragmentos de su hijo la implementen, algo así como
public interface MyChildFragment {
void updateView(int position);
}
Luego, puedes hacer algo como esto para iniciar y actualizar tus fragmentos internos.
Fragment childFragment = (Fragment) mViewPagerDetailsAdapter.instantiateItem(mViewPager,mViewPager.getCurrentItem());
if (childFragment != null) {
((MyChildFragment) childFragment).updateView();
}
PD. Tenga cuidado donde pone ese código, si llama a insatiateItem antes de que el sistema realmente lo cree, el savedInstanceState
de su fragmento hijo será nulo allí
public void onCreate(@Nullable Bundle savedInstanceState){
super(savedInstanceState)
}
Bloqueará su aplicación.
Buena suerte
El escenario en cuestión es mejor atendido por cada Fragmento agregando sus propios elementos de menú y manejando directamente onOptionsItemSelected (), como se describe en la documentación oficial . Es mejor evitar los trucos no documentados.
En mi actividad tengo:
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);
}
En primer lugar, haga un seguimiento de todas las páginas de fragmentos "activos". En este caso, realiza un seguimiento de las páginas de fragmentos en FragmentStatePagerAdapter, que es utilizado por ViewPager.
@Override
public Fragment getItem(int index) {
Fragment myFragment = MyFragment.newInstance();
mPageReferenceMap.put(index, myFragment);
return myFragment;
}
Para evitar mantener una referencia a las páginas de fragmentos "inactivos", debe implementar el método destroyItem (...) de FragmentStatePagerAdapter:
@Override
public void destroyItem (ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mPageReferenceMap.remove(position);
}
y cuando necesita acceder a la página visible actualmente, luego llama:
int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);
Donde el método getFragment (int) de MyAdapter se ve así:
public MyFragment getFragment(int key) {
return mPageReferenceMap.get(key);
}
Espero que pueda ayudar!
Esta es la única forma en que no obtengo NullPointerException para las variables de instancia de esas clases de fragmentos en particular. Esto podría ser útil para otros que se quedaron en la misma situación. En onOptionsItemSelected (), codifiqué de la siguiente manera:
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);
}
Este es el truco más simple:
fun getCurrentFragment(): Fragment? {
return if (count == 0) null
else instantiateItem(view_pager, view_pager.currentItem) as? Fragment
}
(código de kotlin)
Simplemente llame a instantiateItem(viewPager, viewPager.getCurrentItem()
y instantiateItem(viewPager, viewPager.getCurrentItem()
a Fragment
. Su elemento ya se habrá instanciado. Para asegurarse de que puede agregar un cheque para getCount
.
Funciona con FragmentPagerAdapter
y FragmentStatePagerAdapter
!
Esto es más a prueba de futuro que la respuesta aceptada:
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
/* ------------------------------------------------------------------------------------------ */
}
FragmentStatePagerAdapter tiene un método público con el nombre instantiateItem que devuelve su framgment basado en valores de parámetros especificados, este método tiene dos parámetros ViewGroup (ViewPager) y posición.
public Object instantiateItem(ViewGroup container, int position);
Usó este método para obtener el fragmento de posición especificado,
Fragment fragment = (Fragment) adaper.instantiateItem(mViewPager, position);
Este código ayuda para mí, compruébalo.
Hay muchas respuestas aquí que realmente no abordan el hecho básico de que realmente NO HAY manera de hacer esto de manera predecible, y de una manera que no resulte en un disparo en el pie en algún momento en el futuro.
FragmentStatePagerAdapter
es la única clase que sabe cómo acceder confiablemente a los fragmentos que FragmentManager
rastrea: cualquier intento de intentar adivinar la identificación o etiqueta del fragmento no es confiable, a largo plazo. Y los intentos de rastrear las instancias manualmente probablemente no funcionarán bien cuando se guarde / restaure el estado, porque FragmentStatePagerAdapter
bien puede no invocar las retrollamadas cuando restaura el estado.
Lo único que he podido hacer es copiar el código para FragmentStatePagerAdapter
y agregar un método que devuelve el fragmento, dada una posición ( mFragments.get(pos)
). Tenga en cuenta que este método supone que el fragmento está realmente disponible (es decir, fue visible en algún momento).
Si eres particularmente aventurero, puedes usar el reflejo para acceder a los elementos de la lista de mFragments
privados, pero luego volvemos al punto uno (no se garantiza que el nombre de la lista mFragments
siendo el mismo).
He usado lo siguiente:
int index = vpPager.getCurrentItem();
MyPagerAdapter adapter = ((MyPagerAdapter)vpPager.getAdapter());
MyFragment suraVersesFragment = (MyFragment)adapter.getRegisteredFragment(index);
Para obtener el fragmento actual: obtener posición en ViewPager en public void onPageSelected (posición final final) , y luego
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 - argumento de posición asignado en público static PlaceholderFragment newInstance (int sectionNumber); de Fragmento
getChildFragmentManager () o getFragmentManager () - depende de cómo se haya creado SectionsPagerAdapter
Puede definir el PagerAdapter
así y luego podrá obtener cualquier Fragment
en 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);
}
}
Para obtener el Fragment
actual
Fragment currentFragment = mPagerAdapter.getItem(mViewPager.getCurrentItem());
Puede implementar un BroadcastReceiver en el Fragment y enviar un Intent desde cualquier lugar. El receptor del fragmento puede escuchar la acción específica e invocar el método de la instancia.
Una advertencia es asegurarse de que el componente View ya esté instanciado y (y para algunas operaciones, como desplazarse por una lista, el ListView ya debe estar renderizado).
Sé que es demasiado tarde, pero tengo formas muy simples de hacerlo,
// para fragmento en 0 possition
((mFragment) viewPager.getAdapter().instantiateItem(viewPager, 0)).yourMethod();
Si su localizador está dentro de un Fragmento, entonces use esto:
private fun getPagerCurrentFragment(): Fragment? {
return childFragmentManager.findFragmentByTag("android:switcher:${R.id.myViewPagerId}:${myViewPager.currentItem}")
}
Donde R.id.myViewPagerId
es la identificación de su ViewPager dentro del Layout xml.
Simplemente obtenga el elemento actual del buscapersonas y luego pida su adaptador al fragmento de esa posición.
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;
}
Tuve el mismo problema y lo resolví usando este código.
MyFragment fragment = (MyFragment) thisActivity.getFragmentManager().findFragmentById(R.id.container);
Simplemente reemplace el nombre MyFragment con el nombre de su fragmento y agregue el id de su contenedor de fragmento.
FragmentStatePagerAdapter
tiene una variable de instancia privada llamada mCurrentPrimaryItem
de tipo Fragment
. Uno solo puede preguntarse por qué los desarrolladores de Android no le proporcionaron un getter. Esta variable se instancia en el método setPrimaryItem()
. Por lo tanto, anule este método de tal forma que obtenga la referencia a esta variable. Simplemente terminé declarando mi propio mCurrentPrimaryItem
y copiando el contenido de setPrimaryItem()
en mi reemplazo.
En su implementación 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;
}
Fragmento actual:
Esto funciona si creaste un proyecto con la plantilla de tabbar de fragmentos.
Fragment f = mSectionsPagerAdapter.getItem(mViewPager.getCurrentItem());
Tenga en cuenta que esto funciona con la implementación de plantilla de actividad tabulada predeterminada.
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);
}
}
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());
}
});
A veces, el fragmento podría enviar un objeto nulo, por lo que podría ejecutar un Runnable nuevo para resolver el problema en la primera carga.
Gracias rick