Recuperar un fragmento de un ViewPager

Estoy usando un ViewPager junto con un FragmentStatePagerAdapter para alojar tres fragmentos diferentes:

  • [Fragmento1]
  • [Fragmento2]
  • [Fragmento3]

Cuando quiero obtener Fragment1 desde ViewPager en la FragmentActivity .

¿Cuál es el problema y cómo lo soluciono?

La respuesta principal se basa en un nombre generado por el marco. Si eso cambia, entonces ya no funcionará.

¿Qué pasa con esta solución, reemplazando instantiateItem() y destroyItem() de su 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); } } 

Esto parece funcionar para mí cuando se trata de fragmentos que están disponibles. Los fragmentos que aún no han sido instanciados devolverán null al llamar a getRegisteredFragment . Pero he estado usando esto principalmente para obtener el Fragment actual de ViewPager : adapater.getRegisteredFragment(viewPager.getCurrentItem()) y esto no devolverá null .

No tengo conocimiento de otros inconvenientes de esta solución. Si hay alguno, me gustaría saberlo.

Para agarrar fragmentos de un ViewPager hay un montón de respuestas aquí y en otros temas relacionados SO / blogs. Todo el mundo que he visto está roto, y por lo general parecen caer en uno de los dos tipos que se enumeran a continuación. Hay algunas otras soluciones válidas si sólo quieres agarrar el fragmento actual, como esta otra respuesta en este hilo.

Si utiliza FragmentPagerAdapter consulte a continuación. Si utiliza FragmentStatePagerAdapter vale la pena mirar esto . Los índices de agarre que no son los actuales en un FragmentStateAdapter no son tan útiles como por la naturaleza de que estos serán completamente destruidos salió de la vista / fuera de los límites de offScreenLimit.

LOS CAMINOS INFANTILES

Mal: Mantenga su propia lista interna de fragmentos, agregada a cuando se llama FragmentPagerAdapter.getItem()

  • Usando generalmente un SparseArray o un Map
  • No es uno de los muchos ejemplos que he visto cuentas de eventos de ciclo de vida por lo que esta solución es frágil. Como getItem sólo se llama la primera vez que una página se desplaza a (o se obtiene si su ViewPager.setOffscreenPageLimit(x) > 0) en el ViewPager , si la Activity acogida / Fragment es matado o reiniciado entonces el SpaseArray interno será aniquilado cuando Se vuelve a crear la FragmentPagerActivity personalizada, pero detrás de las escenas se recrearán los fragmentos internos de ViewPagers y getItem se getItem a getItem para ninguno de los índices, por lo que la capacidad de obtener un fragmento del índice se perderá para siempre. Puedes dar cuenta de esto ahorrando y restaurando estas referencias de fragmentos a través de FragmentManager.getFragment() y putFragment pero esto comienza a ser IMHO desordenado.

Wrong: Construya su propio id de etiqueta que coincida con lo que se utiliza bajo el capó en FragmentPagerAdapter y utilícelo para recuperar la página Fragments del FragmentManager

  • Esto es mejor en la medida en que se ocupa del problema de las referencias de fragmentos perdidos en la primera solución de matriz interna, pero como se ha señalado correctamente en las respuestas anteriores y en otras partes de la red, se siente hacky como un método privado interno de ViewPager que Podría cambiar en cualquier momento o para cualquier versión del sistema operativo.

El método que se recrea para esta solución es

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

HAPPY PATH: ViewPager.instantiateItem()

Un enfoque similar a getItem() anterior, pero sin romper el ciclo de vida es a esto es conectar en instantiateItem() lugar de getItem() como el primero se llamará cada vez que se crea el índice / se accede. Ver esta respuesta

UN CAMINO FELIZ: Construya su propio FragmentViewPager

Construya su propia clase FragmentViewPager desde el origen de la última lib de soporte y cambie el método utilizado internamente para generar las etiquetas de fragmento. Puede reemplazarlo por el siguiente. Esto tiene la ventaja de que usted sabe que la creación de la etiqueta nunca cambiará y su no confiar en un api / método privado, que siempre es peligroso.

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

A continuación, como dice el doc, cuando quiera agarrar un fragmento usado para un índice, simplemente llame a algo como este método (que puede poner en el FragmentPagerAdapter personalizado o en una subclase) teniendo en cuenta que el resultado puede ser nulo si getItem aún no ha sido llamado Para esa página, es decir, no se ha creado todavía.

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

Esta es una solución simple y resuelve los problemas en las otras dos soluciones que se encuentran por todas partes en la web

Agregue los siguientes métodos a su 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) tiene que funcionar.

Aquí está la solución implementada en ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d . Si algo falla, verá un buen registro de fallos.

Otra solución sencilla:

  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); } } 

Sé que esto tiene algunas respuestas, pero tal vez esto ayudará a alguien. He utilizado una solución relativamente simple cuando necesitaba obtener un Fragment de mi ViewPager . En tu Activity o Fragment sostiene ViewPager , puedes usar este código para recorrer cada Fragment que contiene.

 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. } } 
  • Si conoce la posición de su Fragment en ViewPager , puede llamar a getItem(knownPosition) .

  • Si no conoce la posición de su Fragment en ViewPager , puede hacer que sus hijos Fragments implementen una interfaz con un método como getUniqueId() y utilícelos para diferenciarlos. O puede recorrer todos los Fragments y comprobar el tipo de clase, como if(viewPagerFragment instanceof FragmentClassYouWant)

!!! EDITAR !!!

He descubierto que getItem sólo es llamado por un FragmentPagerAdapter cuando cada Fragment necesita ser creado la primera vez , después de eso, parece que los Fragments se reciclan con el FragmentManager . De esta manera, muchas implementaciones de FragmentPagerAdapter crean nuevos Fragment en getItem . Usando mi método anterior, esto significa que crearemos Fragment nuevos cada vez que getItem se llame mientras pasamos por todos los elementos en el FragmentPagerAdapter . Debido a esto, he encontrado un mejor enfoque, utilizando el FragmentManager para obtener cada Fragment lugar (utilizando la respuesta aceptada). Esta es una solución más completa, y ha funcionado bien para mí.

 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 } } } 

Y necesitará este método.

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

Para mi caso, ninguna de las soluciones anteriores funcionó.

Sin embargo, desde que estoy usando el Fragmento de Hijo en un Fragmento, se utilizó lo siguiente:

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

Esto sólo funcionará si los fragmentos en el Administrador corresponden al elemento viewpager.

Esto se basa en la respuesta de Steven arriba. Esto devolverá la instancia real del fragmento que ya está unido a la actividad padre.

 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 } } } } 

No pude encontrar una manera sencilla y limpia de hacer esto. Sin embargo, el widget ViewPager es sólo otro ViewGroup, que alberga sus fragmentos. ViewPager tiene estos fragmentos como niños inmediatos. Por lo tanto, podría simplemente iterar sobre ellos (usando .getChildCount () y .getChildAt ()), y ver si la instancia de fragmento que está buscando está cargada en ViewPager y obtener una referencia a ella. Por ejemplo, podría utilizar algún campo de ID único estático para distinguir los fragmentos.

Tenga en cuenta que el ViewPager puede no haber cargado el fragmento que está buscando, ya que es un contenedor de virtualización como ListView.

FragmentPagerAdapter es la fábrica de los fragmentos. Para encontrar un fragmento basado en su posición si todavía en la memoria utilice esto:

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

Código de ejemplo para api de soporte v4.

Lo manejé primero haciendo una lista de todos los fragmentos ( List<Fragment> fragments; ) que iba a usar y luego los agregaba al buscapersonas haciendo más fácil manejar el fragmento visto actualmente.

Asi que:

 @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); } 

Así que esto puede llamarse:

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

Así que entonces podría chuck en una declaración if que sólo se ejecutaría si estaba en el fragmento correcto

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

Pero thats apenas mi acercamiento del corte y de la barra diagonal pero trabajó para mí, lo utilizo hacen relevent cambios a mi fragmento exhibido actualmente cuando el botón trasero se empuja.

Para obtener el fragmento Visible actual de ViewPager . Estoy usando esta simple declaración y está funcionando bien.

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

No es necesario llamar a getItem() o algún otro método en una etapa posterior para obtener la referencia de un Fragment alojado dentro de ViewPager . Si desea actualizar algunos datos dentro de Fragment , utilice este enfoque: ¿ Actualizar ViewPager dinámicamente?

La clave es establecer nuevos datos dentro de Adaper y llamar a notifyDataSetChanged() que a su vez llamará getItemPosition() , pasándole una referencia de su Fragment y dándole la oportunidad de actualizarlo. Todas las otras maneras requieren que usted mantenga la referencia a se oa algún otro corte que no es una buena solución.

 @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); } 

La mejor solución es usar la extensión que creamos en CodePath llamada SmartFragmentStatePagerAdapter . Siguiendo esta guía, esto hace que la recuperación de fragmentos y el fragmento actualmente seleccionado de ViewPager sea mucho más fácil. También hace un mejor trabajo de la gestión de la memoria de los fragmentos incrustados en el adaptador.

La forma más fácil y concisa. Si todos sus fragmentos en ViewPager son de clases diferentes, puede recuperarlos y distinguirlos de la siguiente manera:

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

He implementado este fácil con un enfoque un poco diferente.

Mi método FragmentAdapter.getItem personalizado no devolvió MyFragment nuevo (), pero la instancia de MyFragment que se creó en el constructor FragmentAdapter.

En mi actividad, a continuación, tengo el fragmento del adaptador, comprobar si es instanceOf necesario Fragment, a continuación, moldear y utilizar los métodos necesarios.

 Fragment yourFragment = yourviewpageradapter.getItem(int index); 

index es el lugar del fragmento en el adaptador como agregó fragment1 primero así que el fragment1 recuperación del fragment1 pasa como 0 y así sucesivamente para el resto

Cree un id de recurso entero en /values/integers.xml

 <integer name="page1">1</integer> <integer name="page2">2</integer> <integer name="page3">3</integer> 

Luego, en la función getItem de PagerAdapter:

 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; } 

A continuación, en la actividad escribir esta función para obtener referencia fragmento:

 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; } 

Obtenga la referencia de fragmento llamando a la función anterior y luego arrojándola a su fragmento personalizado:

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

Manera fácil de iterar sobre fragmentos en encargado del fragmento. Encuentra viewpager, que tiene argumento de posición de sección, colocado en 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; } 

En Fragmento

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

En 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(); } } 

Debe extender FragmentPagerAdapter a su clase de adaptador ViewPager.
Si utiliza FragmentStatePagerAdapter entonces no podrá encontrar su Fragment por su ID

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

Cómo utilizar este método: –

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

Ok para el adaptador FragmentStatePagerAdapter Fundar una solución:

En su 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); 

Y crear un methode en su clase FragmentActivity – Así que ese método le da acceso a su fragmento, sólo tiene que darle la posición del fragmento que desea:

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

En su adaptador:

 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; } } 

Esta solución fácil funcionó bien para mí.

YourFragment fragment = (YourFragment)getSupportFragmentManager().getFragments().get(0);

(Si conoce su posición de fragmento en la lista, entonces la solución anterior funcionará)

  • Añadir / eliminar páginas a ViewPager dinámicamente
  • Cómo implementar una aplicación de introducción con vista compartida animación
  • Acceda a la vista de fragmentos de padres de ViewPager en el fragmento secundario de Pager
  • Guardar estado de fragmento en ViewPager
  • ViewPager en Android está tomando la pantalla completa?
  • Fragmentos dentro de ViewPager desaparecen cuando cambia la orientación
  • Android.app Fragments vs. android.support.v4.app usando ViewPager?
  • Cómo agregar una vista personalizada en ViewPager para un tutorial
  • Cómo cambiar el color de texto de SlidingTabLayout?
  • FragmentPagerAdapter no está quitando elementos (Fragmentos) correctamente
  • Llenar cada fragmento en ViewPager con otro objeto JSON sin cargar de nuevo
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.