¿Actualizar ViewPager dinámicamente?

316

No puedo actualizar el contenido en ViewPager.

¿Cuál es el uso correcto de los métodos instantiateItem () y getItem () en la clase FragmentPagerAdapter?

Estaba usando solo getItem () para crear instancias y devolver mis fragmentos:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

Esto funcionó bien. Excepto que no puedo cambiar el contenido.

Así que encontré esto: ViewPager PagerAdapter no actualiza la Vista

"Mi enfoque es utilizar el método setTag () para cualquier vista instanciada en el método instantiateItem ()"

Ahora quiero implementar instantiateItem () para hacer eso. Pero no sé qué debo devolver (el tipo es Object) y cuál es la relación con getItem (int position).

Leí la referencia :

  • Fragmento abstracto público getItem (posición int)

    Devuelve el Fragmento asociado con una posición especificada.

  • objeto público instantiateItem (contenedor ViewGroup, posición int)

    Crea la página para el puesto dado. El adaptador es responsable de agregar la vista al contenedor que se proporciona aquí, aunque solo debe asegurarse de que esto se haga para cuando regrese de finishUpdate (ViewGroup). Parámetros

    contenedor La vista que contiene en la que se mostrará la página. posición La posición de la página que se instanciará.

    Devoluciones

    Devuelve un objeto que representa la nueva página. Esto no necesita ser una Vista, pero puede ser otro contenedor de la página.

Pero todavía no lo entiendo.

Aquí está mi código. Estoy usando el paquete de soporte v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pager1);

        pager = (ViewPager)findViewById(R.id.slider);

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

    @Override
    public int getCount() {
        return slideCount;
    }

    public void setData(String[] data) {
        this.data = data;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

Aquí también hay alguien con un problema similar, sin respuestas http://www.mail-archive.com/[email protected]/msg200477.html

Ixx
fuente
Para obtener una mejor comprensión de los componentes involucrados, podría ser útil leer mi tutorial sobre un ViewPager con pestañas con un historial de navegación hacia atrás separado. Viene con un ejemplo de trabajo en GitHub y recursos adicionales: medium.com/@nilan/…
marktani
tengo algún problema stackoverflow.com/questions/40149039/…
albert
Puedes consultar este ejemplo en GitHub . Espero que este ejemplo te ayude.
Cabezas
Buena solución simple: stackoverflow.com/a/35450575/2162226
Gene Bo
Google presentó recientemente ViewPager2 que simplifica las actualizaciones dinámicas de Fragment / View. Puede ver ejemplos en github.com/googlesamples/android-viewpager2 o esta respuesta en stackoverflow.com/a/57393414/1333448
Yves Delerm

Respuestas:

605

Al usar FragmentPagerAdapter o FragmentStatePagerAdapter, es mejor tratar solo getItem()y no tocarlo instantiateItem()en absoluto. La interfaz instantiateItem()- destroyItem()- isViewFromObject()en PagerAdapter es una interfaz de nivel inferior que FragmentPagerAdapter utiliza para implementar una getItem()interfaz mucho más simple .

Antes de entrar en esto, debo aclarar que

si desea cambiar los fragmentos reales que se muestran, debe evitar FragmentPagerAdapter y usar FragmentStatePagerAdapter.

Una versión anterior de esta respuesta cometió el error de usar FragmentPagerAdapter como ejemplo; eso no funcionará porque FragmentPagerAdapter nunca destruye un fragmento después de que se muestra por primera vez.

No recomiendo el setTag()y la findViewWithTag()solución proporcionada en el post se conectó. Como has descubierto, usar setTag()y findViewWithTag()no funciona con fragmentos, por lo que no es una buena combinación.

La solución correcta es anular getItemPosition(). Cuando notifyDataSetChanged()se llama, ViewPager llama getItemPosition()a todos los elementos en su adaptador para ver si necesitan ser movidos a una posición diferente o eliminados.

Por defecto, getItemPosition()devuelve POSITION_UNCHANGED, lo que significa, "Este objeto está bien donde está, no lo destruya ni lo elimine". Volver POSITION_NONEsoluciona el problema diciendo: "Este objeto ya no es un elemento que estoy mostrando, elimínelo". Por lo tanto, tiene el efecto de eliminar y recrear cada elemento de su adaptador.

Esta es una solución completamente legítima! Esta solución hace que notifyDataSetChanged se comporte como un adaptador normal sin ver el reciclaje. Si implementa esta solución y el rendimiento es satisfactorio, está listo para las carreras. Trabajo hecho.

Si necesita un mejor rendimiento, puede usar una getItemPosition()implementación más sofisticada . Aquí hay un ejemplo para un buscapersonas que crea fragmentos a partir de una lista de cadenas:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

Con esta implementación, solo se mostrarán los fragmentos que muestran títulos nuevos. En cambio, cualquier fragmento que muestre títulos que todavía están en la lista se moverá a su nueva posición en la lista, y los fragmentos con títulos que ya no están en la lista serán destruidos.

¿Qué pasa si el fragmento no se ha recreado, pero necesita actualizarse de todos modos? Las actualizaciones de un fragmento vivo se manejan mejor con el fragmento mismo. Esa es la ventaja de tener un fragmento, después de todo, es su propio controlador. Un fragmento puede agregar un oyente u observador a otro objeto onCreate()y luego eliminarlo onDestroy(), administrando así las actualizaciones. No es necesario que coloque todo el código de actualización getItem()como lo hace en un adaptador para ListView u otros tipos de AdapterView.

Una última cosa: solo porque FragmentPagerAdapter no destruye un fragmento no significa que getItemPosition sea completamente inútil en un FragmentPagerAdapter. Todavía puede usar esta devolución de llamada para reordenar sus fragmentos en ViewPager. Sin embargo, nunca los eliminará por completo del FragmentManager.

Bill Phillips
fuente
44
No funciona, todavía no está actualizando nada ... publiqué mi código.
Ixx
64
De acuerdo, descubrí el problema: debes usar FragmentStatePagerAdapter en lugar de FragmentPagerAdapter. FragmentPagerAdapter nunca elimina fragmentos del FragmentManager, solo los separa y los adjunta. Consulte los documentos para obtener más información: en general, solo desea usar FragmentPagerAdapter para fragmentos que son permanentes. He editado mi ejemplo con la corrección.
Bill Phillips
1
Revisaré esto en algún momento más tarde ... gracias por su respuesta. Y luego acepto tu respuesta si funciona. Antes de su último comentario, implementé para eliminar y agregar un nuevo ViewPager cada vez y eso funciona. No es una muy buena solución, pero ahora no tengo tiempo para cambiarla.
Ixx
12
Tienes razón, cambiar la clase del adaptador a FragmentStatePagerAdapter solucionó todos mis problemas. ¡Gracias!
Ixx
2
Incluso cuando devuelvo POSITION_NONE, mientras uso FragmentStatePagerAdapter, puedo ver que elimina mi fragmento y crea la nueva instancia. Pero la nueva instancia recibe un Bundle de estado guardado con el estado de un fragmento antiguo. ¿Cómo puedo destruir completamente el fragmento para que Bundle sea nulo cuando se vuelva a crear?
Cantada el
142

En lugar de regresar POSITION_NONEde getItemPosition()y causando la vista de recreo, hacer esto:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(updateData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

Sus fragmentos deben implementar la UpdateableFragmentinterfaz:

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

y la interfaz:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

Tu clase de datos:

public class UpdateData {
    //whatever you want here
}
M-WaJeEh
fuente
1
¿Significa esto que su MainActivity también necesita implementar la interfaz? Por favor, ayúdame aquí. En mi implementación actual, estoy usando mi clase MainActivity como comunicador entre los fragmentos, por lo que estaba confundido acerca de esta solución.
Rakeeb Rajbhandari
1
Ok, he respondido en otro lugar con implementación completa donde estoy actualizando Fragmentdinámicamente, eche un vistazo: stackoverflow.com/questions/19891828/…
M-WaJeEh
@ M-WaJeEh tal vez algún ejemplo por favor !!! Tengo una vista de lista con la lista de ciudades y después de seleccionar la ciudad abre el visor (3 páginas) con información sobre esta ciudad. funciona bien pero después de seleccionar otro visor de la ciudad muestra que solo la d page on refreshes the 1-st page only after swiping pages? thw 2página 3 d siempre está en blanco. gracias
SERG
44
entre todas las cosas que he visto en los últimos 2.5 días de mi vida laboral ... este es el único que funcionó para mí ... todo tipo de arigato, gracias, spaciba, sukran ... n.
Orkun Ozen
2
Mother of Dragons !, esto funciona de maravilla, la única solución funcionó para mí ... ¡muchas gracias!
Sebastián Guerrero
24

para aquellos que todavía enfrentan el mismo problema que enfrenté antes cuando tengo un ViewPagercon 7 fragmentos. el valor predeterminado para estos fragmentos es cargar el contenido en inglés del APIservicio, pero el problema aquí es que quiero cambiar el idioma de la actividad de configuración y después de finalizar la actividad de configuración quiero ViewPageren la actividad principal actualizar los fragmentos para que coincidan con la selección de idioma del usuario y cargar el contenido en árabe si el usuario elige árabe aquí lo que hice para trabajar desde la primera vez

1- Debe usar FragmentStatePagerAdapter como se mencionó anteriormente.

2- on mainActivity anulo el onResume e hice lo siguiente

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3- getItemPosition()anulé el mPagerAdapter y lo devolví POSITION_NONE.

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
}

funciona como encanto

Ziad Gholmish
fuente
1
Funciona excepto en una situación. Cuando elimina el último elemento en viewPager. Entonces no se elimina de la lista.
Vlado Pandžić
Estaba agregando fragmentos al lado izquierdo y derecho del fragmento actual en el visor dinámicamente. Este funcionó! Gracias.
h8pathak
15

Encontré este problema y finalmente lo resolví hoy, así que escribo lo que he aprendido y espero que sea útil para alguien que es nuevo en Android ViewPagery actualice como lo hago. Estoy usando FragmentStatePagerAdapterAPI nivel 17 y actualmente solo tengo 2 fragmentos. Creo que debe haber algo incorrecto, corrígeme, gracias. ingrese la descripción de la imagen aquí

  1. Los datos serializados deben cargarse en la memoria. Esto se puede hacer usando un CursorLoader/ AsyncTask/ Thread. Si se carga automáticamente depende de su código. Si está utilizando un CursorLoader, se carga automáticamente ya que hay un observador de datos registrado.

  2. Después de llamar viewpager.setAdapter(pageradapter), getCount()se llama constantemente al adaptador para construir fragmentos. Entonces, si se están cargando datos, getCount()puede devolver 0, por lo que no necesita crear fragmentos ficticios para que no se muestren datos.

  3. Después de cargar los datos, el adaptador no generará fragmentos automáticamente ya getCount()que todavía es 0, por lo que podemos establecer el número de datos realmente cargado para que se devuelva getCount()y luego llamar al adaptador notifyDataSetChanged(). ViewPagercomenzar a crear fragmentos (solo los primeros 2 fragmentos) por datos en la memoria. Se hace antes de que notifyDataSetChanged()se devuelva. Entonces ViewPagertiene los fragmentos correctos que necesita.

  4. Si los datos en la base de datos y la memoria se actualizan (escritura), o solo se actualizan los datos en la memoria (escritura), o solo se actualizan los datos en la base de datos. En los últimos dos casos, si los datos no se cargan automáticamente desde la base de datos a la memoria (como se mencionó anteriormente). El ViewPageradaptador de buscapersonas solo se ocupa de los datos en la memoria.

  5. Entonces, cuando se actualizan los datos en la memoria, solo necesitamos llamar al adaptador notifyDataSetChanged(). Como el fragmento ya está creado, onItemPosition()se llamará al adaptador antes de que notifyDataSetChanged()regrese. No hay que hacer nada getItemPosition(). Luego se actualizan los datos.

oscarthecat
fuente
He descubierto que no hay nada que hacer después de actualizar los datos (en memoria), solo llame y notifyDataSetChanged()luego simplemente actualice automáticamente, porque el fragmento (con vista de gráfico) que uso se actualizará. si no es así, como un fragmento estático, tendré que llamar onItemPositon()y devolver lo POSITION_NONE siento por eso
oscarthecat
¿Alguien tiene una idea de qué herramienta se utilizó para hacer ese diagrama?
JuiCe
12

Probar destroyDrawingCache()en ViewPager despuésnotifyDataSetChanged() en su código.

czaku
fuente
3
Igual que aquí. Funciona muy bien gracias. Tuve un momento especialmente difícil porque no estoy usando Fragmentsmi localizador de vistas. ¡Así que gracias!
Seth
11

Después de horas de frustración mientras intentaba todas las soluciones anteriores para superar este problema y también intentaba muchas soluciones en otras preguntas similares como esta , esto y esto que me FALLÓ para resolver este problema y hacer ViewPagerque destruya lo viejo Fragmenty lo llene pagerde el nuevo Fragments. He resuelto el problema de la siguiente manera:

1) Haga que la ViewPagerclase se extienda de la FragmentPagerAdaptersiguiente manera:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Cree un artículo para el ViewPagerque almacena el titley el fragmentsiguiente:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}

3) Haga que el constructor de ViewPagertake my FragmentManagerinstance lo almacene en mi de la classsiguiente manera:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4) Crear un método para re-establecer los adapterdatos con los nuevos datos mediante la supresión de todos los anteriores fragmentde la fragmentManagermisma directamente a hacer el adapterpara establecer la nueva fragmentde la nueva lista de nuevo de la siguiente manera:

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5) Desde el contenedor Activityo Fragmentno reinicialice el adaptador con los nuevos datos. Establezca los nuevos datos a través del método setPagerItemscon los nuevos datos de la siguiente manera:

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();

Espero que ayude.

Sami Eltamawy
fuente
¿me pueden ayudar en mi problema stackoverflow.com/questions/40149039/…
albert
¿pueden ayudarme con este problema? stackoverflow.com/questions/41547995/…
viper
10

Por alguna razón, ninguna de las respuestas funcionó para mí, así que tuve que anular el método restoreState sin llamar a super en mi fragmentStatePagerAdapter. Código:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}
hordurh
fuente
1
Después de probar todo lo demás en esta respuesta SO, esta es la que funcionó para mí. Estoy terminando () una actividad que vuelve a la Actividad que aloja el Adaptador de paginación en cuestión. Quiero que todos mis fragmentos se vuelvan a crear en este caso porque la Actividad que acabo de terminar puede haber cambiado el estado que se utiliza para dibujar los fragmentos.
JohnnyLambada el
OMG ... funciona ... funciona ... felicitaciones: D En mi caso, necesito eliminar el objeto del elemento actual del visor. pero el fragmento actual no se actualiza después de probar todos los métodos / pasos anteriores.
Rahul Khurana
3

Modifiqué ligeramente la solución provista por Bill Phillips para satisfacer mis necesidades

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

para que solo se getItemPosition()devuelvan POSITION_NONElos fragmentos que se encuentran actualmente en el FragmentManagermomento en que getItemPositionse llama. (Tenga en cuenta que esto FragmentStatePagery lo ViewPagerasociado con él están contenidos en un Fragmento no en una Actividad)

adwait
fuente
2

Tuve un problema similar pero no quiero confiar en las soluciones existentes (nombres de etiquetas codificadas, etc.) y no pude hacer que la solución de M-WaJeEh funcionara para mí. Aquí está mi solución:

Mantengo referencias a los fragmentos creados en getItem en una matriz. Esto funciona bien siempre que la actividad no se destruya debido a un cambio de configuración o falta de memoria o lo que sea (-> al volver a la actividad, los fragmentos vuelven a su último estado sin que se vuelva a llamar a 'getItem' y, por lo tanto, sin actualizar la matriz )

Para evitar este problema, implementé instantiateItem (ViewGroup, int) y actualicé mi matriz allí, así:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

Entonces, por un lado, estoy feliz de haber encontrado una solución que funciona para mí y quería compartirla con ustedes, pero también quería preguntar si alguien más intentó algo similar y si hay alguna razón por la que no debería hazlo asi? Hasta ahora funciona muy bien para mí ...

jpm
fuente
He hecho algo relacionado en stackoverflow.com/a/23427215/954643 , aunque probablemente deberías eliminar el elemento de myFragmentsenpublic void destroyItem(ViewGroup container, int position, Object object) para que no tomes una referencia de material obsoleto.
qix
1

Yo uso la biblioteca EventBus para actualizar el Fragmentcontenido ViewPager. La lógica es simple, al igual que el documento de EventBus cómo hacerlo . No es necesario controlar la FragmentPagerAdapterinstancia. El código está aquí:

1: definir eventos

Defina qué mensaje se necesita para actualizar.

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2.Preparar suscriptores

Escriba a continuación el código en el Fragmento que se necesita actualizar.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3. Eventos posteriores

Escriba el siguiente código en otro Activityu otro Fragmentque necesite actualizar el parámetro

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));
Colmillo de fantasía
fuente
1

Si desea utilizar FragmentStatePagerAdapter, consulte https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary% 20 Estrellas y grupoby = & sort = & id = 37990 . Hay problemas con FragmentStatePagerAdapter que pueden o no causar problemas en su caso de uso.

Además, el enlace también tiene pocas soluciones ... pocas pueden adaptarse a sus necesidades.

cgr
fuente
De esa URL encontré la solución. Utilicé el FragmentStatePagerAdapter modificado de este gist gist.github.com/ypresto/8c13cb88a0973d071a64 en mi código y comenzó a funcionar como se suponía.
Rafael
Frio. Si. Link también tiene pocas soluciones.
cgr
1

He vivido el mismo problema y he buscado demasiadas veces. Cualquier respuesta dada en stackoverflow o vía google no fue la solución para mi problema. Mi problema fue fácil. Tengo una lista, muestro esta lista con el visor. Cuando agrego un nuevo elemento al encabezado de la lista y actualizo el visor, nada cambió. Mi solución final fue muy fácil, cualquiera puede usarla. Cuando se agrega un nuevo elemento a la lista y desea actualizar la lista. Primero configure el adaptador del visor en nulo, luego vuelva a crear el adaptador y configúrelo en el visor.

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

Asegúrese de que su adaptador debe extender FragmentStatePagerAdapter

nebyan
fuente
1

Aquí está mi implementación que incorpora la información de @Bill Phillips One obtiene el almacenamiento en caché de fragmentos la mayor parte del tiempo, excepto cuando los datos han cambiado. Simple, y parece funcionar bien.

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);
voam
fuente
0

Había estado probando tantos enfoques diferentes, ninguno realmente solucionó mi problema. A continuación se muestra cómo lo resuelvo con una combinación de soluciones proporcionadas por todos ustedes. Gracias a todos.

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

    @Override
    public int getCount() {
        return 4;
    }

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

Solo quiero actualizar la página 0 después de onResume ().

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

En mi FragmentsMain, hay una "página" entera pública, que me puede decir si es la página que quiero actualizar.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;
Ted Hsieh
fuente
0

Sé que llego tarde a la fiesta. He solucionado el problema llamando TabLayout#setupWithViewPager(myViewPager);justo despuésFragmentPagerAdapter#notifyDataSetChanged();

theapache64
fuente
0

Esta solución no funcionará para todos, pero en mi caso, cada Fragmento en mi ViewPager es una clase diferente, y solo una de ellas existe a la vez.

Con esta restricción, esta solución es segura y debe ser segura de usar en la producción.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

Si tiene varias versiones del mismo fragmento, puede usar esta misma estrategia para llamar a métodos en esos fragmentos para determinar si es el fragmento que desea actualizar.

Kevin Grant
fuente
0

Esto podría ser de ayuda para alguien: en mi caso, al insertar una nueva página, el localizador de vistas solicitaba la posición de un fragmento existente dos veces, pero no solicitaba la posición del nuevo elemento, lo que causaba un comportamiento incorrecto y no se mostraban los datos.

  1. Copie la fuente de FragmentStatePagerAdapter (parece que no se ha actualizado en años).

  2. Anular notifyDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Agregue una comprobación de cordura para destroyItem () para evitar bloqueos:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }
Hombre malo
fuente
0

Revisé todas las respuestas anteriores y algunas otras publicaciones, pero aún no pude encontrar algo que funcionó para mí (con diferentes tipos de fragmentos junto con la adición y eliminación dinámica de pestañas). El siguiente enfoque de FWIW es lo que funcionó para mí (en caso de que alguien más tenga los mismos problemas).

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


    public MyFragmentStatePageAdapter(FragmentManager fm) {
        super(fm);
    }


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


    @Override
    public int getCount() {
        return titles.size();
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        fragmentPositions.remove(object);
    }
}
John O'Reilly
fuente
0

Use FragmentStatePagerAdapter en lugar de FragmentPagerAdapter si desea volver a crear o recargar un fragmento en función del índice. Por ejemplo, si desea recargar un fragmento que no sea FirstFragment, puede verificar la instancia y devolver la posición de esta manera

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }
HemangNirmal
fuente
0

Necesita cambiar el elemento getItemPosition de instantiateItem's mFragments.

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

Basado en AndroidX FragmentStatePagerAdapter.java, porque mFragmentsla posición de los elementos no cambia al llamar a notifyDataSetChanged ().

Fuente: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Ejemplo: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

Puede ejecutar este proyecto para confirmar cómo funciona. https://github.com/cuichanghao/infivt

changhao cui
fuente