Estoy usando ViewPager de la biblioteca de compatibilidad. Lo he conseguido mostrando varias vistas que puedo ver.
Sin embargo, estoy teniendo dificultades para descubrir cómo actualizar ViewPager con un nuevo conjunto de Vistas.
He intentado todo tipo de cosas como llamar mAdapter.notifyDataSetChanged()
, mViewPager.invalidate()
incluso crear un nuevo adaptador cada vez que quiero usar una nueva Lista de datos.
Nada ha ayudado, las vistas de texto permanecen sin cambios con respecto a los datos originales.
Actualización: hice un pequeño proyecto de prueba y casi he podido actualizar las vistas. Pegaré la clase a continuación.
Sin embargo, lo que no parece actualizarse es la segunda vista, la 'B' permanece, debería mostrar 'Y' después de presionar el botón de actualización.
public class ViewPagerBugActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
Button updateButton = (Button) findViewById(R.id.update_button);
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateViewPager();
}
});
}
private void updateViewPager() {
data.clear();
data.add("X");
data.add("Y");
data.add("Z");
myViewPager.getAdapter().notifyDataSetChanged();
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
fuente
Respuestas:
Hay varias formas de lograr esto.
La primera opción es más fácil, pero un poco más ineficiente.
Anular
getItemPosition
en tuPagerAdapter
como este:De esta forma, cuando llame
notifyDataSetChanged()
, el localizador de vistas eliminará todas las vistas y las volverá a cargar todas. Así se obtiene el efecto de recarga.La segunda opción, sugerida por Álvaro Luis Bustamante (anteriormente alvarolb) , es utilizar el
setTag()
método parainstantiateItem()
crear una nueva vista. Luego, en lugar de usarnotifyDataSetChanged()
, puede usarfindViewWithTag()
para encontrar la vista que desea actualizar.El segundo enfoque es muy flexible y de alto rendimiento. Felicitaciones a alvarolb por la investigación original.
fuente
No creo que haya ningún tipo de error en el
PagerAdapter
. El problema es que entender cómo funciona es un poco complejo. Mirando las soluciones explicadas aquí, hay un malentendido y, por lo tanto, un mal uso de las vistas instanciadas desde mi punto de vista.Los últimos días he estado trabajando con
PagerAdapter
yViewPager
, y encontré lo siguiente:El
notifyDataSetChanged()
método en elPagerAdapter
solo notificaráViewPager
que las páginas subyacentes han cambiado. Por ejemplo, si ha creado / eliminado páginas dinámicamente (agregando o eliminando elementos de su lista),ViewPager
debe ocuparse de eso. En este caso, creo queViewPager
determina si una nueva vista debe eliminarse o instanciarse utilizando los métodosgetItemPosition()
ygetCount()
.Creo que
ViewPager
, después de que unanotifyDataSetChanged()
llamada toma sus vistas secundarias y comprueba su posición con elgetItemPosition()
. Si para una vista secundaria este método regresaPOSITION_NONE
,ViewPager
comprende que la vista ha sido eliminada, invocandodestroyItem()
y eliminando esta vista.De esta manera, anular el
getItemPosition()
retorno siemprePOSITION_NONE
es completamente incorrecto si solo desea actualizar el contenido de las páginas, porque las vistas creadas previamente se destruirán y se crearán nuevas cada vez que llamenotifyDatasetChanged()
. Puede parecer que no está tan mal solo por unos pocosTextView
segundos, pero cuando tiene vistas complejas, como ListViews pobladas de una base de datos, esto puede ser un problema real y una pérdida de recursos.Por lo tanto, hay varios enfoques para cambiar de manera eficiente el contenido de una vista sin tener que eliminar e instanciar la vista nuevamente. Depende del problema que quieras resolver. Mi enfoque es usar el
setTag()
método para cualquier vista instanciada en elinstantiateItem()
método. Entonces, cuando desee cambiar los datos o invalidar la vista que necesita, puede llamar alfindViewWithTag()
método enViewPager
para recuperar la vista previamente instanciada y modificarla / usarla como desee sin tener que eliminar / crear una nueva vista cada vez que desee para actualizar algún valor.Imagine, por ejemplo, que tiene 100 páginas con 100
TextView
sy solo desea actualizar un valor periódicamente. Con los enfoques explicados anteriormente, esto significa que está eliminando e instanciando 100TextView
s en cada actualización. No tiene sentido...fuente
setTag
métodoonInstantiateItem
. ¿Quieres actualizar esta respuesta con algo de codificación? Gracias.Cambiar el
FragmentPagerAdapter
aFragmentStatePagerAdapter
.getItemPosition()
Método de anulación y devoluciónPOSITION_NONE
.Eventualmente, escuchará el
notifyDataSetChanged()
localizador en vista.fuente
La respuesta dada por alvarolb es definitivamente la mejor manera de hacerlo. Sobre la base de su respuesta, una manera fácil de implementar esto es simplemente almacenar las vistas activas por posición:
Luego, una vez anulando el
notifyDataSetChanged
método, puede actualizar las vistas ...En realidad, puede usar un código similar en
instantiateItem
ynotifyDataSetChanged
para actualizar su vista. En mi código, uso exactamente el mismo método.fuente
Tuve el mismo problema Para mí, funcionó para extender FragmentStatePagerAdapter y anular los siguientes métodos:
fuente
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 todos FALLARON conmigo para resolver este problema y hacer
ViewPager
que destruya lo viejoFragment
y lo llenepager
de el nuevoFragment
s. He resuelto el problema de la siguiente manera:1) Haga que la
ViewPager
clase se extienda de laFragmentPagerAdapter
siguiente manera:2) Cree un artículo para el
ViewPager
que almacena eltitle
y elfragment
siguiente:3) Haga que el constructor de
ViewPager
take myFragmentManager
instance lo almacene en mi de laclass
siguiente manera:4) Crear un método para re-establecer los
adapter
datos con los nuevos datos mediante la supresión de todos los anterioresfragment
de lafragmentManager
misma directamente a hacer eladapter
para establecer la nuevafragment
de la nueva lista de nuevo de la siguiente manera:5) Desde el contenedor
Activity
oFragment
no reinicialice el adaptador con los nuevos datos. Establezca los nuevos datos a través del métodosetPagerItems
con los nuevos datos de la siguiente manera:Espero que ayude.
fuente
Tuve el mismo problema y mi solución está usando
FragmentPagerAdapter
anulandoFragmentPagerAdapter#getItemId(int position)
:Por defecto, este método devuelve la posición del artículo. Supongo que
ViewPager
comprueba siitemId
se cambió y recrea la página solo si fue así. Pero la versiónitemId
no anulada devuelve la misma posición que incluso si la página es realmente diferente, y ViewPager no define que la página se reemplaza por una y necesita ser recreada.Para usar esto,
long id
es necesario para cada página. Normalmente se espera que sea único, pero sugiero, para este caso, que solo sea diferente del valor anterior para la misma página. Entonces, es posible usar un contador continuo en el adaptador o enteros aleatorios (con amplia distribución) aquí.Creo que es una forma más consistente en lugar de usar las etiquetas de vista mencionadas como una solución en este tema. Pero probablemente no para todos los casos.
fuente
PagerAdapter
. Que clase es estaFragmentPagerAdapter
Encontré una decisión muy interesante de este problema. En lugar de usar FragmentPagerAdapter , que guarda en la memoria todos los fragmentos, podemos usar FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ), que vuelve a cargar el fragmento cada vez que lo seleccionamos.
Las realizaciones de ambos adaptadores son idénticas. Entonces, solo necesitamos cambiar " extender FragmentPagerAdapter " en " extender FragmentStatePagerAdapter "
fuente
Después de mucho buscar este problema, encontré una solución realmente buena que creo que es la forma correcta de hacerlo. Esencialmente, instantiateItem solo se llama cuando la vista se instancia y nunca más a menos que la vista se destruya (esto es lo que sucede cuando se anula la función getItemPosition para devolver POSITION_NONE). En cambio, lo que desea hacer es guardar las vistas creadas y actualizarlas en el adaptador, generar una función get para que otra persona pueda actualizarla, o una función establecida que actualice el adaptador (mi favorito).
Entonces, en su MyViewPagerAdapter agregue una variable como:
An en su instancia de artículo:
así, de esta manera, puede crear una función que actualizará su vista:
¡Espero que esto ayude!
fuente
instantiateItem
, por eso mis vistas no se actualizaban. De tu respuesta me di cuenta. +1Dos años y medio después de que el OP planteara su pregunta, este problema sigue siendo, bueno, sigue siendo un problema. Es obvio que la prioridad de Google en esto no es particularmente alta, así que en lugar de encontrar una solución, encontré una solución. El gran avance para mí fue descubrir cuál era la verdadera causa del problema (vea la respuesta aceptada en esta publicación ). Una vez que se hizo evidente que el problema era que las páginas activas no se actualizaban correctamente, mi solución era obvia:
En mi Fragmento (las páginas):
En mi actividad, donde hago la carga de las páginas:
Después de esto, cuando vuelva a cargar un segundo conjunto de páginas, el error aún hará que algunos muestren los datos antiguos. Sin embargo, ahora se actualizarán y verá los nuevos datos: sus usuarios no sabrán que la página nunca fue incorrecta porque esta actualización ocurrirá antes de que vean la página.
¡Espero que esto ayude a alguien!
fuente
Toda esta solución no me ayudó. Por lo tanto, encontré una solución que funciona: siempre puedes
setAdapter
, pero no es suficiente. debe hacer esto antes de cambiar el adaptador:y después de esto:
fuente
ViewPager
fragmento interior, así que lo reemplacéslideShowPagerAdapter.getFragmentManager()
congetChildFragmentManager()
. TalgetFragmentManager()
vez ayude en su caso. Yo solíaFragmentPagerAdapter
, noFragmentStatePagerAdapter
. También vea stackoverflow.com/a/25994654/2914140 para una manera hacky.Una forma mucho más fácil: use
FragmentPagerAdapter
ay ajuste sus vistas paginadas en fragmentos. Se actualizanfuente
Gracias rui.araujo y Alvaro Luis Bustamante. Al principio, trato de usar rui.araujo, porque es fácil. Funciona, pero cuando cambian los datos, la página se volverá a dibujar obviamente. Es malo, así que trato de usar el camino de Álvaro Luis Bustamante. Es perfecto. Aquí está el código:
Y cuando los datos cambian:
fuente
En caso de que alguien esté usando el adaptador basado en FragmentStatePagerAdapter (que permitirá a ViewPager crear páginas mínimas necesarias para el propósito de visualización, como máximo 2 para mi caso), la respuesta de @ rui.araujo de sobrescribir getItemPosition en su adaptador no causará un desperdicio significativo, pero aún así puede ser mejorado.
En pseudocódigo:
fuente
getItemPosition()
cuando el conjunto de datos cambió, y ViewPager sabrá que han cambiado o no. lamentablemente, ViewPager no lo hizo.Tuve un problema similar en el que tenía cuatro páginas y una de las páginas actualizaba las vistas de las otras tres. Pude actualizar los widgets (SeekBars, TextViews, etc.) en la página adyacente a la página actual. Las últimas dos páginas tendrían widgets no inicializados al llamar
mTabsAdapter.getItem(position)
.Para resolver mi problema, solía
setSelectedPage(index)
antes de llamargetItem(position)
. Esto crearía una instancia de la página, lo que me permite poder modificar valores y widgets en cada página.Después de toda la actualización que usaría
setSelectedPage(position)
seguido denotifyDataSetChanged()
.Puede ver un ligero parpadeo en ListView en la página principal de actualización, pero no se nota nada. No lo he probado completamente, pero resuelve mi problema inmediato.
fuente
Solo estoy publicando esta respuesta en caso de que alguien más lo encuentre útil. Para hacer exactamente lo mismo, simplemente tomé el código fuente de ViewPager y PagerAdapter de la biblioteca de compatibilidad y lo compilé dentro de mi código (debe resolver todos los errores e importarlos usted mismo, pero definitivamente se puede hacer).
Luego, en CustomViewPager, cree un método llamado updateViewAt (int position). La vista en sí se puede obtener de los elementos de ArrayList definidos en la clase ViewPager (debe establecer un Id. Para las vistas en el elemento instanciado y comparar este Id. Con la posición en el método updateViewAt ()). Luego puede actualizar la vista según sea necesario.
fuente
Supongo que tengo las lógicas de ViewPager.
Si necesito actualizar un conjunto de páginas y mostrarlas en función del nuevo conjunto de datos, llamo a notifyDataSetChanged () . Luego, ViewPager realiza una serie de llamadas a getItemPosition () , pasando allí Fragment como Object. Este Fragmento puede ser de un conjunto de datos antiguo (que quiero descartar) o de uno nuevo (que quiero mostrar). Entonces, anulo getItemPosition () y allí tengo que determinar de alguna manera si mi Fragmento es del conjunto de datos anterior o del nuevo.
En mi caso, tengo un diseño de 2 paneles con una lista de elementos superiores en el panel izquierdo y una vista de deslizamiento (ViewPager) a la derecha. Por lo tanto, guardo un enlace a mi elemento superior actual dentro de mi Adaptador Pager y también dentro de cada Fragmento de página instanciado. Cuando cambia el elemento superior seleccionado en la lista, almaceno el nuevo elemento superior en PagerAdapter y llamo a notifyDataSetChanged () . Y en el getItemPosition () reemplazado , comparo el elemento superior de mi adaptador con el elemento superior de mi fragmento. Y solo si no son iguales, devuelvo POSITION_NONE. Luego, PagerAdapter reinstala todos los fragmentos que han devuelto POSITION_NONE.
NOTA.Almacenar la identificación del elemento superior en lugar de una referencia podría ser una mejor idea.
El fragmento de código a continuación es un poco esquemático, pero lo adapté del código que realmente funciona.
¡Gracias por todos los investigadores anteriores!
fuente
El siguiente código funcionó para mí.
Cree una clase que extienda la clase FragmentPagerAdapter como se muestra a continuación.
Luego, dentro de cada Fragmento que creaste, crea un método updateFragment. En este método, cambia las cosas que necesita cambiar en el fragmento. Por ejemplo, en mi caso, Fragment0 contenía un GLSurfaceView que muestra un objeto 3D basado en una ruta a un archivo .ply, por lo que dentro de mi método updateFragment cambio la ruta a este archivo de capas.
luego cree una instancia de ViewPager,
y una instancia de Adpater,
entonces haz esto,
Luego, dentro de la clase, inicializaste la clase Adaptador anterior y creaste un viewPager, cada vez que quieras actualizar uno de tus fragmentos (en nuestro caso Fragment0) usa lo siguiente:
Esta solución se basó en la técnica sugerida por Alvaro Luis Bustamante.
fuente
1.Primero debe configurar el método getItemposition en su clase Pageradapter 2.Debe leer la posición Exacta de su Visualizador de páginas 3.A continuación, envíe esa posición como ubicación de datos de su nuevo 4.Escriba el botón de actualización haciendo clic en el oyente dentro del setonPageChange oyente
ese código de programa se modificó un poco para configurar solo el elemento de posición particular
fuente
lo que funcionó para mí iba
viewPager.getAdapter().notifyDataSetChanged();
y en el adaptador colocando su código para actualizar la vista dentro de esta
getItemPosition
maneraPuede que no sea la forma más correcta de hacerlo, pero funcionó (el
return POSITION_NONE
truco me causó un colapso, así que no era una opción)fuente
Puede actualizar dinámicamente todos los fragmentos, puede ver en tres pasos.
En tu adaptador:
Ahora en tu actividad:
Finalmente en tu fragmento, algo así:
Puedes ver el código completo aquí .
Gracias Alvaro Luis Bustamante.
fuente
Siempre volviendo
POSITION_NONE
es simple pero un poco ineficiente porque evoca la creación de instancias de todas las páginas que ya se han creado.He creado una biblioteca ArrayPagerAdapter para cambiar elementos en PagerAdapters dinámicamente.
Internamente, los adaptadores de esta biblioteca regresan
POSITION_NONE
engetItemPosiition()
sólo cuando sea necesario.Puede cambiar elementos dinámicamente como los siguientes utilizando esta biblioteca.
La biblioteca Thils también admite páginas creadas por Fragments.
fuente
Esto es para todos aquellos como yo, que necesitan actualizar el Viewpager desde un servicio (u otro hilo de fondo) y ninguna de las propuestas ha funcionado: después de un poco de verificación, me di cuenta de que el método notifyDataSetChanged () nunca regresa. getItemPosition (Object object) se llama all all allí sin procesamiento adicional. Luego encontré en los documentos de la clase padre PagerAdapter (no está en los documentos de las subclases), "Los cambios en el conjunto de datos deben ocurrir en el hilo principal y deben terminar con una llamada a notifyDataSetChanged ()". Entonces, la solución de trabajo en este caso fue (usando FragmentStatePagerAdapter y getItemPosition (Object object) configurado para devolver POSITION_NONE):
y luego la llamada a notifyDataSetChanged ():
fuente
Puede agregar la transformación de buscapersonas en Viewpager de esta manera
En el siguiente código, cambié el color de mi vista en tiempo de ejecución cuando se desplaza el localizador
fuente
Sé que llego tarde pero aún así puede ayudar a alguien. Solo estoy extendiendo la respuesta acertada y también he agregado el comentario al respecto.
bien,
la respuesta en sí dice que es ineficiente
así que para que se actualice solo cuando sea necesario, puede hacer esto
fuente
ViewPager no fue diseñado para admitir cambios dinámicos de vista.
Tuve confirmación de esto mientras buscaba otro error relacionado con este https://issuetracker.google.com/issues/36956111 y en particular https://issuetracker.google.com/issues/36956111#comment56
Esta pregunta es un poco antigua, pero Google recientemente resolvió este problema con ViewPager2 . Permitirá reemplazar las soluciones hechas a mano (sin mantenimiento y potencialmente defectuosas) por soluciones estándar. También evita recrear vistas innecesariamente como lo hacen algunas respuestas.
Para ver ejemplos de ViewPager2, puede consultar https://github.com/googlesamples/android-viewpager2
Si desea usar ViewPager2, deberá agregar la siguiente dependencia en su archivo build.gradle:
Luego puede reemplazar su ViewPager en su archivo xml con:
Después de eso, deberá reemplazar ViewPager por ViewPager2 en su actividad
ViewPager2 necesita un RecyclerView.Adapter o un FragmentStateAdapter, en su caso puede ser un RecyclerView.Adapter
En el caso de que estuviera usando un TabLayout, puede usar un TabLayoutMediator:
Luego podrá actualizar sus vistas modificando los datos de su adaptador y llamando al método notifyDataSetChanged
fuente
En lugar de regresar
POSITION_NONE
y crear todos los fragmentos nuevamente, puede hacer lo que sugerí aquí: ¿ Actualizar ViewPager dinámicamente?fuente
Creo que he hecho una manera simple de notificar los cambios en el conjunto de datos:
Primero, cambie un poco la forma en que funciona la función instantiateItem:
para "updateView", llene la vista con todos los datos que desea llenar (setText, setBitmapImage, ...).
Verifique que destroyView funcione así:
Ahora, suponga que necesita cambiar los datos, hacerlo y luego llamar a la siguiente función en el Adaptador de Pager:
Por ejemplo, si desea notificar a todas las vistas que viewPager muestra que algo ha cambiado, puede llamar a:
Eso es.
fuente
Por lo que vale, en KitKat + parece que
adapter.notifyDataSetChanged()
es suficiente para que aparezcan las nuevas vistas, siempre que sea losetOffscreenPageLimit
suficientemente alto. Soy capaz de obtener el comportamiento deseado haciendoviewPager.setOffscreenPageLimit(2)
.fuente
En realidad yo uso
notifyDataSetChanged()
enViewPager
yCirclePageIndicator
, y después de eso que llamodestroyDrawingCache()
elViewPager
y funciona .. Ninguna de las otras soluciones que funcionó para mí.fuente