android - with - Recuperar un fragmento de un ViewPager
tablayout without fragments (23)
Estoy usando un ViewPager
junto con un FragmentStatePagerAdapter
para alojar tres fragmentos diferentes:
- [Fragmento 1]
- [Fragmento 2]
- [Fragmento 3]
Cuando quiero obtener Fragment1
desde ViewPager
en FragmentActivity
.
¿Cuál es el problema y cómo lo soluciono?
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())));
}
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ás un buen registro de fallos.
Bien para el adaptador FragmentStatePagerAdapter
I financiar una solución:
en tu 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 cree un método en su clase FragmentActivity - Para que ese método le brinde acceso a su fragmento, solo necesita 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;
}
}
Crear 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;
}
Luego en la actividad escribe esta función para obtener la referencia del 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 del fragmento llamando a la función anterior y luego conviértala en su fragmento personalizado:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
Debe extender FragmentPagerAdapter
en su clase de adaptador ViewPager.
Si usa FragmentStatePagerAdapter
, 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;
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();
}
}
En TabLayout hay múltiples pestañas para Fragmento. Puede encontrar el fragmento por etiqueta utilizando el índice del fragmento.
Por ej. el índice para Fragment1 es 0, por lo que en el método findFragmentByTag()
, pase la etiqueta para Viewpager. Después de usar fragmentTransaction puede agregar, reemplace el fragmento.
String tag = "android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Esto se basa en la respuesta de Steven anterior. Esto devolverá la instancia real del fragmento que ya está adjunto a la actividad principal.
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
}
}
}
}
FragmentPagerAdapter es la fábrica de los fragmentos. Para encontrar un fragmento basado en su posición si aún está en la memoria, use esto:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Código de muestra para v4 soporte api.
Hola, he respondido a esta pregunta here . Básicamente, necesitas anular
Objeto público instantiateItem (ViewGroup container, int position)
Método de FragmentStatePagerAdapter.
Implementé esto fácil con un enfoque un poco diferente.
Mi método personalizado FragmentAdapter.getItem no devolvió MyFragment () nuevo, sino la instancia de MyFragment que se creó en el constructor FragmentAdapter.
En mi actividad, luego obtuve el fragmento del adaptador, verifique si se trata de un fragmento de instancia necesario, luego emita y use los métodos necesarios.
La forma más fácil y concisa. Si todos sus fragmentos en ViewPager
son de diferentes clases, 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;
}
}
}
La mejor solución es usar la extensión que creamos en CodePath llamada SmartFragmentStatePagerAdapter . Siguiendo esa guía, esto hace que la recuperación de fragmentos y el fragmento seleccionado actualmente de un ViewPager sea mucho más fácil. También hace un mejor trabajo de administrar la memoria de los fragmentos incrustados en el adaptador.
La respuesta principal se basa en un nombre generado por el marco. Si eso cambia alguna vez, entonces ya no funcionará.
¿Qué pasa con esta solución, anulando 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 disponibles. Los fragmentos que aún no se han instanciado, devolverán un valor nulo al llamar a getRegisteredFragment
. Pero lo he estado utilizando principalmente para obtener el Fragment
actual del ViewPager
: adapater.getRegisteredFragment(viewPager.getCurrentItem())
y esto no devolverá el null
.
No tengo conocimiento de ningún otro inconveniente de esta solución. Si hay alguno, me gustaría saberlo.
Lo manejé haciendo primero una lista de todos los fragmentos (enumerar List<Fragment> fragments;
) que iba a usar y luego los agregué al buscapersonas, lo que facilita el manejo del fragmento que se está viendo 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);
}
entonces esto puede ser llamado:
public Fragment getFragment(ViewPager pager){
Fragment theFragment = fragments.get(pager.getCurrentItem());
return theFragment;
}
así que entonces podría lanzarlo en una sentencia if que solo se ejecutaría si estuviera 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 eso es solo mi enfoque de corte y corte, pero funcionó para mí, lo uso para hacer cambios relevantes en mi fragmento que se muestra actualmente cuando se presiona el botón Atrás.
Manera fácil de iterar sobre fragmentos en el gestor de fragmentos. Busque viewpager, que tiene un argumento de posición de sección, colocado en PlaceInstance público de PlaceholderFragment (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;
}
No necesita 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
, use este enfoque: ¿ Actualizar ViewPager dinámicamente?
La clave es establecer nuevos datos dentro de Adaper
y llamar a notifyDataSetChanged()
que a su vez llamará a getItemPosition()
, pasándole una referencia de su Fragment
y dándole la oportunidad de actualizarlo. Todas las demás formas requieren que se guarde una referencia a usted mismo oa algún otro hack que no sea 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);
}
No pude encontrar una manera simple y limpia de hacer esto. Sin embargo, el widget ViewPager es solo otro ViewGroup, que alberga sus fragmentos. El ViewPager tiene estos fragmentos como hijos inmediatos. Por lo tanto, podría iterar sobre ellos (utilizando .getChildCount () y .getChildAt ()), y ver si la instancia del fragmento que está buscando está cargada actualmente en ViewPager y obtener una referencia a la misma. Por ejemplo, podría usar algún campo de ID único estático para distinguir los fragmentos.
Tenga en cuenta que ViewPager puede no haber cargado el fragmento que está buscando, ya que es un contenedor de virtualización como ListView.
Otra solución simple:
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);
}
}
Para capturar fragmentos de un ViewPager hay muchas respuestas aquí y en otros hilos / blogs de SO relacionados. Todos los que he visto están rotos, y en general parecen caer en uno de los dos tipos que se enumeran a continuación. Hay otras soluciones válidas si solo desea capturar el fragmento actual, como this otra respuesta en este hilo.
Si está utilizando FragmentPagerAdapter
ver más abajo. Si usar FragmentStatePagerAdapter
vale la pena mirar this . La captura de índices que no son los actuales en FragmentStateAdapter no es tan útil, ya que por su naturaleza, estos se desarmarán por completo y quedaron fuera de la vista / fuera de los límites de ScreenSimreenLimit.
Los trazos infelices
Incorrecto: Mantenga su propia lista interna de fragmentos, agregada a cuando se llama a FragmentPagerAdapter.getItem()
- Usualmente usando un
SparseArray
o unMap
- No es uno de los muchos ejemplos que he visto en las cuentas de eventos del ciclo de vida, por lo que esta solución es frágil. Como solo se llama a
getItem
la primera vez que una página se desplaza a (o se obtiene siViewPager.setOffscreenPageLimit(x)
> 0) enViewPager
, si laActivity
/Fragment
delSpaseArray
seSpaseArray
o reinicia, elSpaseArray
interno seSpaseArray
cuando el FragmentPagerActivity personalizado se recrea, pero entre bambalinas se recrearán los fragmentos internos de ViewPagers, ygetItem
segetItem
agetItem
para ninguno de los índices, por lo que la capacidad de obtener un fragmento del índice se perderá para siempre. Puede explicar esto guardando y restaurando estas referencias de fragmentos a través deFragmentManager.getFragment()
yputFragment
pero esto comienza aputFragment
IMHO.
Incorrecto: construya su propia identificación de etiqueta que coincida con lo que se usa debajo del capó en FragmentPagerAdapter
y use esto para recuperar los fragmentos de la página desde FragmentManager
- Esto es mejor en la medida en que resuelve el problema de la pérdida de fragmentos y referencias en la primera solución de matriz interna, pero como se señala correctamente en las respuestas anteriores y en otros lugares de la red, se siente como un método privado interno de
ViewPager
que Podría cambiar en cualquier momento o para cualquier versión de 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;
}
UN CAMINO FELIZ: ViewPager.instantiateItem()
Un enfoque similar a getItem()
anterior, pero que no rompe el ciclo de vida, es el de getItem()
instantiateItem()
lugar de getItem()
ya que el primero se llamará cada vez que se cree / acceda al índice. Ver esta respuesta
UN CAMINO FELIZ: Construye tu propio FragmentViewPager
Construya su propia clase FragmentViewPager
desde el origen de la última biblioteca de soporte y cambie el método utilizado internamente para generar las etiquetas de fragmentos. Puedes reemplazarlo con el siguiente. Esto tiene la ventaja de que sabe que la creación de la etiqueta nunca cambiará y que no confía en un API / método privado, lo 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;
}
Luego, como indica el documento, cuando desee capturar un fragmento utilizado para un índice, simplemente llame a algo como este método (que puede colocar en el FragmentPagerAdapter
personalizado o una subclase) teniendo en cuenta que el resultado puede ser nulo si aún no se ha llamado getItem para esa página, es decir, aún no ha sido creado.
/**
* @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 en todas partes en la web
Para mi caso, ninguna de las soluciones anteriores funcionó.
Sin embargo, dado que estoy usando el administrador de fragmentos secundarios en un fragmento, se utilizó lo siguiente:
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
Esto solo funcionará si sus fragmentos en el Administrador corresponden al elemento del viewpager.
Sé que esto tiene algunas respuestas, pero tal vez esto ayude a alguien. He utilizado una solución relativamente simple cuando necesitaba obtener un Fragment
de mi ViewPager
. En su Activity
o Fragment
sostiene el ViewPager
, puede 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 elViewPager
, simplemente puede llamar agetItem(knownPosition)
.Si no conoce la posición de su
Fragment
en elViewPager
, puede hacer que losFragments
getUniqueId()
implementen una interfaz con un método comogetUniqueId()
, y usarla para diferenciarlos. O puede alternar entre todos losFragments
y verificar el tipo de clase, como por ejemploif(viewPagerFragment instanceof FragmentClassYouWant)
!!! EDITAR !!!
Descubrí que el getItem
solo llama a getItem
cuando se debe crear cada Fragment
la primera vez , después de eso, parece que los Fragments
se reciclan utilizando el FragmentManager
. De esta manera, muchas implementaciones de FragmentPagerAdapter
crean nuevos Fragment
en getItem
. Usando mi método anterior, esto significa que crearemos nuevos Fragment
cada vez que se getItem
medida que revisamos todos los elementos del FragmentPagerAdapter
. Debido a esto, he encontrado un mejor enfoque, utilizando FragmentManager
para obtener cada Fragment
lugar (utilizando la respuesta aceptada). Esta es una solución más completa y me ha funcionado bien.
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ás este método.
private static String makeFragmentName(int viewId, int position) {
return "android:switcher:" + viewId + ":" + position;
}
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
es el lugar del fragmento en el adaptador, como usted agregó fragment1
primero, así que recupere el index
pase de fragment1
como 0 y así sucesivamente para el resto