Respuestas:
La respuesta principal se basa en un nombre generado por el marco. Si eso cambia, ya no funcionará.
¿Qué pasa con esta solución, primordial 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 se han instanciado, volverán nulos al llamar getRegisteredFragment
. Pero he estado usando esto principalmente para obtener la corriente Fragment
del ViewPager
: adapater.getRegisteredFragment(viewPager.getCurrentItem())
y esto no volverá null
.
No conozco ningún otro inconveniente de esta solución. Si hay alguno, me gustaría saberlo.
MyPagerAdapter
se destruye debido al ciclo de vida (es decir, rotar), no registerdFragments
se perderá? ¿Tendrá que usar el Activity
/ Fragment
using MyPagerAdapter
para guardarlo onSaveInstanceState
y luego deberá actualizarlo con la nueva FragmentManager
referencia?
getItem
no se vuelve a llamar al rotar (para los fragmentos que ya se crearon), ya que FragmentManager
restaura los estados de los Fragments
que contiene en el buscapersonas. Si instantiateItem
se llama cuando Fragment
se restaura cada uno , entonces esta solución es, de hecho, más segura y una prueba más futura que la mía o la respuesta aceptada. Consideraré probar esto yo mismo.
Para obtener fragmentos de un ViewPager hay muchas respuestas aquí y en otros hilos / blogs SO relacionados. Todos los que he visto están rotos, y generalmente parecen caer en uno de los dos tipos enumerados a continuación. Hay algunas otras soluciones válidas si solo desea obtener el fragmento actual, como esta otra respuesta en este hilo.
Si está utilizando FragmentPagerAdapter
ver más abajo. Si FragmentStatePagerAdapter
vale la pena mirar esto . Los índices de captura que no son los actuales en un FragmentStateAdapter no son tan útiles, ya que por su naturaleza, estos se eliminarán por completo y quedarán fuera de la vista / fuera de los límites fuera de la pantalla.
Incorrecto: mantenga su propia lista interna de fragmentos, agregada a cuándo FragmentPagerAdapter.getItem()
se llama
SparseArray
oMap
getItem
solo se llama la primera vez que se desplaza una página (u se obtiene si su ViewPager.setOffscreenPageLimit(x)
> 0) en el ViewPager
, si el alojamiento Activity
/ Fragment
se elimina o se reinicia, el interno se SpaseArray
borrará cuando se vuelva a crear la FragmentPagerActivity personalizada, pero detrás de escena ViewPagers fragmentos internos serán recreados, y getItem
se NO ser llamados por cualquiera de los índices, por lo que la posibilidad de obtener un fragmento del índice se perderán para siempre. Puede explicar esto guardando y restaurando estas referencias de fragmentos a través de FragmentManager.getFragment()
y, putFragment
pero esto comienza a complicarse en mi humilde opinión.Incorrecto: construya su propia identificación de etiqueta que coincida con la que se usa debajo del capóFragmentPagerAdapter
y úsela para recuperar los fragmentos de página delFragmentManager
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;
}
ViewPager.instantiateItem()
Un enfoque similar al getItem()
anterior pero que no rompe el ciclo de vida es que se conecte en instantiateItem()
lugar de getItem()
como se llamará al primero cada vez que se cree / acceda a ese índice. Ver esta respuesta
FragmentViewPager
Construya su propia FragmentViewPager
clase a partir de la fuente de la última lib de soporte y cambie el método utilizado internamente para generar las etiquetas de fragmentos. Puede reemplazarlo con el siguiente. Esto tiene la ventaja de que sabe que la creación de etiquetas nunca cambiará y que no dependerá de una 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;
}
Luego, como dice el documento, cuando desee obtener un fragmento utilizado para un índice, simplemente llame a algo como este método (que puede poner en la costumbre FragmentPagerAdapter
o en una subclase) teniendo en cuenta que el resultado puede ser nulo si getItem aún no se ha solicitado esa página, es decir, aún no se ha 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
instantiateItem()
que es cuando desea acceder a un fragmento que aún no se ha visitado después de que se haya producido un evento de ciclo de vida y se haya visitado antes del evento de ciclo de vida. Sé una pequeña diferencia, pero es la que condujo a un error sutil en mi programa, por lo tanto, estoy investigando esto.
getItemId(pos)
adentroFragmentStatePagerAdapter
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 bloqueo.
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);
}
}
setPrimaryItem()
se llama después ViewPager.OnPageChangeListener#onPageSelected()
no puedo usarlo :-(
Sé que esto tiene algunas respuestas, pero tal vez esto ayude a alguien. He usado una solución relativamente simple cuando necesitaba obtener una Fragment
de mi ViewPager
. En su Activity
o Fragment
sosteniendo el ViewPager
, puede usar este código para recorrer cada uno de los 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 el ViewPager
, simplemente puede llamar getItem(knownPosition)
.
Si no conoce la posición de su Fragment
en el ViewPager
, puede hacer que sus hijos Fragments
implementen una interfaz con un método como getUniqueId()
, y lo usen para diferenciarlos. O puede recorrer todo Fragments
y verificar el tipo de clase, comoif(viewPagerFragment instanceof FragmentClassYouWant)
!!! EDITAR !!!
He descubierto que getItem
solo recibe una llamada de a FragmentPagerAdapter
cuando cada uno Fragment
debe crearse la primera vez , después de eso, parece que Fragments
se reciclan con FragmentManager
. De esta manera, muchas implementaciones de FragmentPagerAdapter
crear nuevos Fragment
s en getItem
. Usando mi método anterior, esto significa que crearemos nuevos correos Fragment
electrónicos cada vez que getItem
se llama a medida que avanzamos por todos los elementos del FragmentPagerAdapter
. Debido a esto, he encontrado un mejor enfoque, usando el FragmentManager
para obtener cada uno Fragment
(usando 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;
}
ViewPager
mismo establece esas etiquetas. Esta solución es frágil porque se basa en conocer la convención de nomenclatura "oculta" de ViewPager
. La respuesta de Streets of Boston es una solución mucho más completa, que ahora uso en mi proyecto (en lugar de este enfoque).
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 visor.
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())));
}
Lo manejé primero haciendo una lista de todos los fragmentos ( List<Fragment> fragments;
) que iba a usar y luego los agregué al buscapersonas para que sea más fácil manejar el fragmento que se ve actualmente.
Entonces:
@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 se puede llamar:
public Fragment getFragment(ViewPager pager){
Fragment theFragment = fragments.get(pager.getCurrentItem());
return theFragment;
}
entonces podría tirarlo en una declaración 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 ese es solo mi enfoque de pirateo y corte, pero funcionó para mí, lo uso hacer cambios relevantes a mi fragmento actualmente mostrado cuando se presiona el botón Atrás.
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
}
}
}
}
No pude encontrar una manera simple y limpia de hacer esto. Sin embargo, el widget ViewPager es solo otro ViewGroup, que aloja sus fragmentos. ViewPager tiene estos fragmentos como elementos secundarios inmediatos. Por lo tanto, puede iterar sobre ellos (usando .getChildCount () y .getChildAt ()), y ver si la instancia de fragmento que está buscando está cargada actualmente en ViewPager y obtener una referencia. 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.
FragmentPagerAdapter es la fábrica de los fragmentos. Para encontrar un fragmento basado en su posición si todavía 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 support api.
No necesita llamar ni ningúngetItem()
otro método en una etapa posterior para obtener la referencia de un Fragment
host alojado dentro ViewPager
. Si desea actualizar algunos datos dentro Fragment
, utilice este enfoque: ¿ Actualizar ViewPager dinámicamente?
La clave es establecer nuevos datos dentro Adaper
y llamar, lo notifyDataSetChanged()
que a su vez lo llamará getItemPosition()
, pasando una referencia de usted Fragment
y dándole la oportunidad de actualizarlo. Todas las otras formas requieren que mantengas referencia a ti mismo o algún otro truco 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);
}
getItemPosition()
Es un método en su adaptador. No lo llamarás directamente. Tiene una referencia de su adaptador, por lo que debe llamar, lo adapter.notifyDataSetChanged()
que a su vez llamará getItemPosition()
a su adaptador pasando la referencia de su Fragment
s. Puede ver una implementación completa en acción aquí stackoverflow.com/questions/19891828/…
Fragment
pero la forma de obtener una referencia es debatible. Otras soluciones obtienen referencia al FragmentManager
usar una cadena similar "android:switcher:"+id
o al volver POSITION_NONE
de getItemosition()
causar que todos los fragmentos se vuelvan a crear.
Debe extenderse FragmentPagerAdapter
a su clase de adaptador ViewPager.
Si usa, FragmentStatePagerAdapter
entonces no podrá encontrar su Fragment
por suID
public static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
Cómo usar este método: -
Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
Hola, he respondido esta pregunta aquí . Básicamente, debes anular
Objeto público instantiateItem (contenedor ViewGroup, posición int)
Método de FragmentStatePagerAdapter.
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 significativamente más fácil. También hace un mejor trabajo al administrar la memoria de los fragmentos incrustados dentro del adaptador.
La forma más fácil y concisa. Si todos sus fragmentos 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;
}
}
}
Implementé esto fácilmente con un enfoque un poco diferente.
Mi método FragmentAdapter.getItem personalizado no devolvió MyFragment () nuevo, sino la instancia de MyFragment que se creó en el constructor FragmentAdapter.
En mi actividad, obtuve el fragmento del adaptador, verifico si es una instancia del Fragmento necesario, luego lanzo y uso los métodos necesarios.
Crear 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 PagerAdapter getItem:
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, escriba esta función para obtener una referencia de 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 transmítala a su fragmento personalizado:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
Manera fácil de iterar sobre fragmentos en el administrador de fragmentos. Busque el visor, que tiene un argumento de posición de sección, colocado en un marcador de posición estático público 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 FragmentoActividad
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 varias pestañas para Fragment. puede encontrar el fragmento por etiqueta utilizando el índice del fragmento.
Por ej. el índice para Fragment1 es 0, por lo que, en el findFragmentByTag()
método, pase la etiqueta para Viewpager. Después de usar fragmentTransaction, puede agregar y reemplazar el fragmento.
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Ok para el adaptador FragmentStatePagerAdapter
, financio 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 dé acceso a su Fragment, 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;
}
}
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
es el lugar del fragmento en el adaptador, como agregó fragment1
primero, así que fragment1
pase de nuevo index
como 0 y así sucesivamente para descansar
Fragment
de unaWeakReference
para garantizar que no impiden un fragmento destruido de ser recogido de basura? Parece que es lo correcto ...