ViewPager PagerAdapter no actualiza la vista

Estoy usando ViewPager de la biblioteca de compatibilidad. He logrado mostrar varias vistas a través de las cuales puedo pasar la página.

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 a mAdapter.notifyDataSetChanged() , mViewPager.invalidate() incluso creando un nuevo adaptador cada vez que quiero utilizar una nueva lista de datos.

Nada ha ayudado, las textviews permanecen sin cambios desde 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, debe mostrar ‘Y’ después de presionar el botón de actualización.

 public class ViewPagerBugActivity extends Activity { private ViewPager myViewPager; private List data; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); data = new ArrayList(); 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 data; private Context ctx; public MyViewPagerAdapter(Context ctx, List 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 restreState(Parcelable arg0, ClassLoader arg1) { } @Override public void startUpdate(View arg0) { } @Override public void finishUpdate(View arg0) { } } } 

Hay varias formas de lograr esto.

La primera opción es más fácil, pero un poco más ineficiente.

getItemPosition en su PagerAdapter esta manera:

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

De esta forma, cuando llame a notifyDataSetChanged() , el buscapersonas eliminará todas las vistas y las volverá a cargar todas. Como tal, se obtiene el efecto de recarga.

La segunda opción, sugerida por Alvaro Luis Bustamante (anteriormente alvarolb) , es setTag() método setTag() en instantiateItem() al crear una nueva vista. Luego, en lugar de usar notifyDataSetChanged() , puede usar findViewWithTag() 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.

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 uso deficiente de las vistas creadas desde mi punto de vista.

Los últimos días he estado trabajando con PagerAdapter y ViewPager , y encontré lo siguiente:

El método notifyDataSetChanged() en el PagerAdapter solo notificará a 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 debería encargarse de eso. En este caso, creo que ViewPager determina si una nueva vista debe eliminarse o getItemPosition() instancia utilizando los getItemPosition() y getCount() .

Creo que ViewPager , después de una llamada notifyDataSetChanged() toma sus vistas secundarias y verifica su posición con getItemPosition() . Si para una vista secundaria, este método devuelve POSITION_NONE , ViewPager entiende que la vista se ha eliminado, llamando a destroyItem() y eliminando esta vista.

De esta forma, anular getItemPosition() para devolver siempre POSITION_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 llame a notifyDatasetChanged() . Puede parecer que no está tan mal solo para algunos TextView , pero cuando tienes vistas complejas, como ListViews pobladas de una base de datos, esto puede ser un problema real y un desperdicio de recursos.

Por lo tanto, hay varios enfoques para cambiar de forma eficiente el contenido de una vista sin tener que eliminar y crear una instancia de la vista de nuevo. Depende del problema que quieras resolver. Mi enfoque es usar el método setTag() para cualquier vista instanciada en el método instantiateItem() . Entonces, cuando quiera cambiar los datos o invalidar la vista que necesita, puede llamar al método findViewWithTag() en ViewPager para recuperar la vista previamente instanciada y modificarla / usarla como desee sin tener que eliminar / crear una nueva vista cada vez que quiera actualizar algún valor.

Imagine, por ejemplo, que tiene 100 páginas con 100 TextView y solo desea actualizar un valor periódicamente. Con los enfoques explicados anteriormente, esto significa que está eliminando e instanciando 100 TextView en cada actualización. No tiene sentido…

Cambia el FragmentPagerAdapter a FragmentStatePagerAdapter .

getItemPosition() método getItemPosition() y devuelva POSITION_NONE .

Eventualmente, escuchará notifyDataSetChanged() en el notifyDataSetChanged() de la vista.

La respuesta dada por alvarolb es definitivamente la mejor manera de hacerlo. Basándose en su respuesta, una forma fácil de implementar esto es simplemente almacenar las vistas activas por posición:

 SparseArray views = new SparseArray(); @Override public Object instantiateItem(View container, int position) { View root = ; ((ViewPager) container).addView(root); views.put(position, root); return root; } @Override public void destroyItem(View collection, int position, Object o) { View view = (View)o; ((ViewPager) collection).removeView(view); views.remove(position); view = null; } 

Luego, una vez al anular el método notifyDataSetChanged , puede actualizar las vistas …

 @Override public void notifyDataSetChanged() { int key = 0; for(int i = 0; i < views.size(); i++) { key = views.keyAt(i); View view = views.get(key);  } super.notifyDataSetChanged(); } 

En realidad, puede usar un código similar en notifyDataSetChanged y notifyDataSetChanged para actualizar su vista. En mi código uso exactamente el mismo método.

Tenía el mismo problema. Para mí, funcionó para extender FragmentStatePagerAdapter y anular los siguientes métodos:

 @Override public Parcelable saveState() { return null; } @Override public void restreState(Parcelable state, ClassLoader loader) { } 

Después de horas de frustración mientras probaba todas las soluciones anteriores para superar este problema y también probaba muchas soluciones sobre otras preguntas similares como esta , esto y esto que ViewPager conmigo para resolver este problema y hacer que ViewPager destruyera el antiguo Fragment y llene el pager con el nuevo Fragment s. He resuelto el problema de la siguiente manera:

1) Haga que la clase ViewPager extienda FragmentPagerAdapter siguiente manera:

  public class myPagerAdapter extends FragmentPagerAdapter { 

2) Cree un elemento para ViewPager que almacene el title y el fragment siguiente manera:

 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) Hacer que el constructor de ViewPager tome mi instancia de FragmentManager para almacenarla en mi class siguiente manera:

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

4) Cree un método para restablecer los datos del adapter con los nuevos datos eliminando todo el fragment anterior del propio fragmentManager directamente para hacer que el adapter vuelva a establecer el nuevo fragment de la nueva lista de la siguiente manera:

 public void setPagerItems(ArrayList 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 Activity o Fragment no reiniciar el adaptador con los nuevos datos. Establezca los nuevos datos a través del método setPagerItems con los nuevos datos de la siguiente manera:

 ArrayList pagerItems = new ArrayList(); pagerItems.add(new PagerItem("Fragment1", new MyFragment1())); pagerItems.add(new PagerItem("Fragment2", new MyFragment2())); mPagerAdapter.setPagerItems(pagerItems); mPagerAdapter.notifyDataSetChanged(); 

Espero que ayude.

Dos años y medio después de que el OP hiciera su pregunta, este problema sigue siendo, bueno, todavía 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 (ver la respuesta aceptada en este post ). Una vez que era evidente que el problema era que las páginas activas no se actualizan adecuadamente, mi solución fue obvia:

En mi Fragmento (las páginas):

  • Tomé todo el código que rellena el formulario de onCreateView y lo puse en una función llamada PopulateForm que se puede llamar desde cualquier lugar, en lugar de desde el marco. Esta función intenta obtener la Vista actual usando getView, y si eso es nulo, simplemente regresa. Es importante que PopulateForm contenga solo el código que se muestra; el rest del código que crea los detectores de FocusChange y similares todavía está en OnCreate.
  • Cree un booleano que se pueda usar como un indicador que indique que el formulario se debe volver a cargar. El mío es mbReloadForm
  • Reemplazar OnResume () para llamar a PopulateForm () si se establece mbReloadForm.

En mi Actividad, donde hago la carga de las páginas:

  • Vaya a la página 0 antes de cambiar cualquier cosa. Estoy usando FragmentStatePagerAdapter, así que sé que dos o tres páginas se ven afectadas como máximo. Cambiar a la página 0 asegura que solo tenga el problema en las páginas 0, 1 y 2.
  • Antes de borrar la lista anterior, tome su tamaño (). De esta forma sabrá cuántas páginas se ven afectadas por el error. Si es> 3, reduzca a 3: si usa un PagerAdapter diferente, tendrá que ver cuántas páginas tiene que tratar (¿quizás todas?)
  • Recargue los datos y llame a pageAdapter.notifyDataSetChanged ()
  • Ahora, para cada una de las páginas afectadas, vea si la página está activa usando pager.getChildAt (i): esto le indica si tiene una vista. Si es así, llame a pager.PopulateView (). Si no, configure el indicador ReloadForm.

Después de esto, cuando vuelvas a cargar un segundo conjunto de páginas, el error seguirá causando 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 fue incorrecta alguna vez porque esta actualización ocurrirá antes de que vean la página.

¡Espero que esto ayude a alguien!

Una forma mucho más sencilla: use un FragmentPagerAdapter y ajuste sus vistas paginado en fragmentos. Ellos se actualizan

En caso de que alguien esté utilizando el adaptador basado en FragmentStatePagerAdapter (que le permitirá a ViewPager crear páginas mínimas necesarias para su visualización, como máximo 2 para mi caso), la respuesta de @ rui.araujo de sobreescribir getItemPosition en su adaptador no causará desperdicio significativo, pero aún así puede ser mejorado.

En pseudo código:

 public int getItemPosition(Object object) { YourFragment f = (YourFragment) object; YourData d = f.data; logger.info("validate item position on page index: " + d.pageNo); int dataObjIdx = this.dataPages.indexOf(d); if (dataObjIdx < 0 || dataObjIdx != d.pageNo) { logger.info("data changed, discard this fragment."); return POSITION_NONE; } return POSITION_UNCHANGED; } 

Tuve el mismo problema y mi solución reemplaza a ViewPagerAdapter#getItemId(int position) :

 @Override public long getItemId(int position) { return mPages.get(position).getId(); } 

Por defecto, este método devuelve la posición del elemento. Supongo que ViewPager comprueba si itemId se modificó y recrea la página solo si lo fue. Pero la versión no reemplazada devuelve la misma posición que itemId incluso si la página es realmente diferente, y ViewPager no define que la página sea reemplazada y necesita ser recreada.

Para usar esto, se necesita una long id para cada página. Normalmente se espera que sea único, pero sugiero, para este caso, que simplemente sea diferente del valor anterior para la misma página. Por lo tanto, es posible usar contador continuo en adaptador o enteros aleatorios (con amplia distribución) aquí.

Creo que es una forma más consistente que el uso de tags de vista mencionadas como una solución en este tema. Pero probablemente no para todos los casos.

Después de mucho buscar este problema, encontré una solución realmente buena que creo que es la forma correcta de hacerlo. Básicamente, instanciateItem solo se llama cuando se crea una instancia de la vista 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 quiere 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 de configuración que actualice el adaptador (mi favorito).

Entonces, en su MyViewPagerAdapter agregue una variable como:

 private View updatableView; 

un en su elemento instantáneo:

  public Object instantiateItem(View collection, int position) { updatableView = new TextView(ctx); //My change is here view.setText(data.get(position)); ((ViewPager)collection).addView(view); return view; } 

entonces, de esta manera, puedes crear una función que actualizará tu vista:

 public updateText(String txt) { ((TextView)updatableView).setText(txt); } 

¡Espero que esto ayude!

Tuve un problema similar en el que tenía cuatro páginas y una de las páginas con vistas actualizadas en 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 sin inicializar al llamar a mTabsAdapter.getItem(position) .

Para resolver mi problema, utilicé setSelectedPage(index) antes de llamar a getItem(position) . Esto crearía una instancia de la página, permitiéndome poder alterar valores y widgets en cada página.

Después de toda la actualización, usaría setSelectedPage(position) seguido de notifyDataSetChanged() .

Puede ver un ligero parpadeo en el ListView en la página principal de actualización, pero nada notable. No lo he probado a fondo, pero sí resuelve mi problema inmediato.

Acabo de publicar esta respuesta en caso de que alguien más la 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 y se importa a sí mismo, pero definitivamente se puede hacer).

Luego, en CustomViewPager, cree un método llamado updateViewAt (int position). La vista en sí misma puede obtenerse 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.

Todas estas soluciones no me ayudaron. así encontré una solución de trabajo: puede setAdapter cada vez, pero no es suficiente. deberías hacer esto antes de cambiar el adaptador:

 FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); List fragments = fragmentManager.getFragments(); for (Fragment f : fragments) { transaction.remove(f); } transaction.commit(); 

y después de esto:

 viewPager.setAdapter(adapter); 

Gracias rui.araujo y Alvaro Luis Bustamante. Al principio, trato de usar rui.araujo, porque es fácil. Funciona, pero cuando los datos cambian, la página se volverá a dibujar obviamente. Es malo, así que trato de usar el modo de Álvaro Luis Bustamante. Es perfecto. Aquí está el código:

 @Override protected void onStart() { super.onStart(); } private class TabPagerAdapter extends PagerAdapter { @Override public int getCount() { return 4; } @Override public boolean isViewFromObject(final View view, final Object object) { return view.equals(object); } @Override public void destroyItem(final View container, final int position, final Object object) { ((ViewPager) container).removeView((View) object); } @Override public Object instantiateItem(final ViewGroup container, final int position) { final View view = LayoutInflater.from( getBaseContext()).inflate(R.layout.activity_approval, null, false); container.addView(view); ListView listView = (ListView) view.findViewById(R.id.list_view); view.setTag(position); new ShowContentListTask(listView, position).execute(); return view; } } 

Y cuando los datos cambian:

 for (int i = 0; i < 4; i++) { View view = contentViewPager.findViewWithTag(i); if (view != null) { ListView listView = (ListView) view.findViewById(R.id.list_view); new ShowContentListTask(listView, i).execute(); } } 

Encontré una decisión muy interesante de este problema. En lugar de utilizar FragmentPagerAdapter , que conserva todos los fragmentos en la memoria, 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

lo que funcionó para mí fue ir a viewPager.getAdapter().notifyDataSetChanged();

y en el adaptador poner su código para actualizar la vista dentro de getItemPosition como tal

 @Override public int getItemPosition(Object object) { if (object instanceof YourViewInViewPagerClass) { YourViewInViewPagerClass view = (YourViewInViewPagerClass)object; view.setData(data); } return super.getItemPosition(object); } 

podría no ser la forma más correcta de hacerlo, pero funcionó (el truco return POSITION_NONE causó un locking para mí, así que no era una opción)

Puede actualizar dinámicamente todos los fragmentos, puede verlos en tres pasos.

En tu adaptador:

 public class MyPagerAdapter extends FragmentPagerAdapter { private static int NUM_ITEMS = 3; private Map mFragmentTags; private FragmentManager mFragmentManager; public MyPagerAdapter(FragmentManager fragmentManager) { super(fragmentManager); mFragmentManager = fragmentManager; mFragmentTags = new HashMap(); } // Returns total number of pages @Override public int getCount() { return NUM_ITEMS; } // Returns the fragment to display for that page @Override public Fragment getItem(int position) { switch (position) { case 0: return FirstFragment.newInstance(); case 1: return SecondFragment.newInstance(); case 2: return ThirdFragment.newInstance(); default: return null; } } // Returns the page title for the top indicator @Override public CharSequence getPageTitle(int position) { return "Page " + position; } @Override public Object instantiateItem(ViewGroup container, int position) { Object object = super.instantiateItem(container, position); if (object instanceof Fragment) { Fragment fragment = (Fragment) object; String tag = fragment.getTag(); mFragmentTags.put(position, tag); } return object; } public Fragment getFragment(int position) { Fragment fragment = null; String tag = mFragmentTags.get(position); if (tag != null) { fragment = mFragmentManager.findFragmentByTag(tag); } return fragment; }} 

Ahora en tu actividad:

 public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{ MyPagerAdapter mAdapterViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager); mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(mAdapterViewPager); viewPager.addOnPageChangeListener(this); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { Fragment fragment = mAdapterViewPager.getFragment(position); if (fragment != null) { fragment.onResume(); } } @Override public void onPageScrollStateChanged(int state) { }} 

Finalmente en tu fragmento, algo así:

 public class YourFragment extends Fragment { // newInstance constructor for creating fragment with arguments public static YourFragment newInstance() { return new YourFragment(); } // Store instance variables based on arguments passed @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } // Inflate the view for the fragment based on layout XML @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment, container, false); } @Override public void onResume() { super.onResume(); //to refresh your view refresh(); }} 

Puedes ver el código completo aquí .

Gracias Alvaro Luis Bustamante.

Siempre devolver POSITION_NONE es simple pero un poco ineficiente porque eso evoca la creación de instancias de todas las páginas que ya han creado una instancia.

Creé una biblioteca ArrayPagerAdapter para cambiar elementos en PagerAdapters dinámicamente.

Internamente, los adaptadores de esta biblioteca devuelven POSITION_NONE en getItemPosiition() solo cuando es necesario.

Puede cambiar los elementos dinámicamente como los siguientes utilizando esta biblioteca.

 @Override protected void onCreate(Bundle savedInstanceState) { /** ... **/ adapter = new MyStatePagerAdapter(getSupportFragmentManager() , new String[]{"1", "2", "3"}); ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter); adapter.add("4"); adapter.remove(0); } class MyPagerAdapter extends ArrayViewPagerAdapter { public MyPagerAdapter(String[] data) { super(data); } @Override public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) { View v = inflater.inflate(R.layout.item_page, container, false); ((TextView) v.findViewById(R.id.item_txt)).setText(item); return v; } } 

La biblioteca Thils también admite páginas creadas por Fragments.

Este es un problema horrible y estoy feliz de presentar una excelente solución; simple, eficiente y efectivo!

Vea a continuación, el código muestra usando una bandera para indicar cuándo devolver POSITION_NONE

 public class ViewPagerAdapter extends PagerAdapter { // Members private boolean mForceReinstantiateItem = false; // This is used to overcome terrible bug that Google isn't fixing // We know that getItemPosition() is called right after notifyDataSetChanged() // Therefore, the fix is to return POSITION_NONE right after the notifyDataSetChanged() was called - but only once @Override public int getItemPosition(Object object) { if (mForceReinstantiateItem) { mForceReinstantiateItem = false; return POSITION_NONE; } else { return super.getItemPosition(object); } } public void setData(ArrayList newContent) { mDisplayContent = newContent; mForceReinstantiateItem = true; notifyDataSetChanged(); } } 

Supongo que tengo las lógicas de ViewPager.

Si necesito actualizar un conjunto de páginas y mostrarlas en base al nuevo conjunto de datos, llamo notifyDataSetChanged () . Luego, ViewPager realiza una serie de llamadas a getItemPosition () , pasando Fragment como un Objeto. 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 dos paneles con una lista de elementos principales en el panel izquierdo y una vista deslizante (ViewPager) a la derecha. Por lo tanto, guardo un enlace a mi elemento superior actual dentro de mi PagerAdapter y también dentro de cada Fragmento de página instanciado. Cuando el elemento superior seleccionado en la lista cambia, almaceno el nuevo elemento superior en PagerAdapter y llamo notifyDataSetChanged () . Y en la reemplazada getItemPosition () comparo el ítem superior de mi adaptador con el ítem 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 siguiente fragmento de código es un poco esquemático, pero lo adapté del código que realmente funciona.

 public class SomeFragment extends Fragment { private TopItem topItem; } public class SomePagerAdapter extends FragmentStatePagerAdapter { private TopItem topItem; public void changeTopItem(TopItem newTopItem) { topItem = newTopItem; notifyDataSetChanged(); } @Override public int getItemPosition(Object object) { if (((SomeFragment) object).getTopItemId() != topItem.getId()) { return POSITION_NONE; } return super.getItemPosition(object); } } 

¡Gracias a todos los investigadores anteriores!

Esto es para todos los que como yo, que necesitan actualizar el Viewpager de un servicio (u otro hilo de fondo) y ninguna de las propuestas ha funcionado: después de un poco de logchecking me di cuenta de que el método notifyDataSetChanged () nunca regresa. getItemPosition (Object object) se llama all all all ends sin procesamiento adicional. Luego encontré en los documentos de la clase principal PagerAdapter (no está en los documentos de las subclases), “Los cambios en el conjunto de datos deben ocurrir en el hilo principal y deben finalizar con una llamada a notifyDataSetChanged ()”. Entonces, la solución de trabajo en este caso era (usando FragmentStatePagerAdapter y getItemPosition (objeto Objeto) configurado para devolver POSITION_NONE):

y luego la llamada a notifyDataSetChanged ():

 runOnUiThread(new Runnable() { @Override public void run() { pager.getAdapter().notifyDataSetChanged(); } }); 

El siguiente código funcionó para mí.

Cree una clase que amplíe la clase FragmentPagerAdapter como se muestra a continuación.

 public class Adapter extends FragmentPagerAdapter { private int tabCount; private Activity mActivity; private Map mFragmentTags; private FragmentManager mFragmentManager; private int container_id; private ViewGroup container; private List object; public Adapter(FragmentManager fm) { super(fm); } public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) { super(fm); mActivity = mA; mFragmentManager = fm; object = new ArrayList<>(); mFragmentTags = new HashMap(); this.tabCount = numberOfTabs; } @Override public Fragment getItem(int position) { switch (position) { case 0: return Fragment0.newInstance(mActivity); case 1: return Fragment1.newInstance(mActivity); case 2: return Fragment2.newInstance(mActivity); default: return null; }} @Override public Object instantiateItem(ViewGroup container, int position) { Object object = super.instantiateItem(container, position); if (object instanceof Fragment) { Log.e("Already defined","Yes"); Fragment fragment = (Fragment) object; String tag = fragment.getTag(); Log.e("Fragment Tag","" + position + ", " + tag); mFragmentTags.put(position, tag); }else{ Log.e("Already defined","No"); } container_id = container.getId(); this.container = container; if(position == 0){ this.object.add(0,object); }else if(position == 1){ this.object.add(1,object); }else if(position == 2){ this.object.add(2,object); } return object; } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); if (object instanceof Fragment) { Log.e("Removed" , String.valueOf(position)); } } @Override public int getItemPosition (Object object) { int index = 0; if(this.object.get(0) == object){ index = 0; }else if(this.object.get(1) == object){ index = 1; }else if(this.object.get(2) == object){ index = 2; }else{ index = -1; } Log.e("Index" , "..................." + String.valueOf(index)); if (index == -1) return POSITION_NONE; else return index; } public String getFragmentTag(int pos){ return "android:switcher:"+R.id.pager+":"+pos; } public void NotifyDataChange(){ this.notifyDataSetChanged(); } public int getcontainerId(){ return container_id; } public ViewGroup getContainer(){ return this.container; } public List getObject(){ return this.object; } @Override public int getCount() { return tabCount; }} 

Then inside each Fragment you created, create an updateFragment method. In this method you change the things you need to change in the fragment. For example in my case, Fragment0 contained a GLSurfaceView which displays a 3d object based on a path to a .ply file, so inside my updateFragment method I change the path to this ply file.

then create a ViewPager instance,

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

and an Adpater instance,

 adapter = new Adapter(getSupportFragmentManager(), 3, this); 

then do this,

 viewPager.setAdapter(adapter); viewPager.setOffscreenPageLimit(1); 

Then inside the class were you initialized the Adapter class above and created a viewPager, every time you want to update one of your fragments (in our case Fragment0) use the following:

 adapter.NotifyDataChange(); adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager. fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0. fragment0.updateFragment() method which include the updates on this fragment adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0. 

This solution was based on the technique suggested by Alvaro Luis Bustamante.

I have tried all this solutions did not work for me because all my views are complex. So it is difficult to save the View and update the View. If you have a simple View that contains only a few TextView or something like this, it’s easy and the solutions posted here works. But if you have complex View like mine, and need to change the entire Fragment, I think that’s impossible to do. I have investigating the ViewPager code, and it seems that the ViewPager keeps the old fragments. It’s easy to realize it : Search for some native application from your device that implements swipe and do the test changing the language from English to Arabic. In this case, it should change the order of fragments, because Arabic is a RTL language. But it did not work this way.

Instead of returning POSITION_NONE and creating all fragments again, you can do as I suggested here: Update ViewPager dynamically?

1.First you have to set the getItemposition method in your Pageradapter class 2.You have to read the Exact position of your View Pager 3.then send that position as data location of your new one 4.Write update button onclick listener inside the setonPageChange listener

that program code is little bit i modified to set the particular position element only

 public class MyActivity extends Activity { private ViewPager myViewPager; private List data; public int location=0; public Button updateButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); data = new ArrayList(); data.add("A"); data.add("B"); data.add("C"); data.add("D"); data.add("E"); data.add("F"); myViewPager = (ViewPager) findViewById(R.id.pager); myViewPager.setAdapter(new MyViewPagerAdapter(this, data)); updateButton = (Button) findViewById(R.id.update); myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i2) { //Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show(); } @Override public void onPageSelected( int i) { // here you will get the position of selected page final int k = i; updateViewPager(k); } @Override public void onPageScrollStateChanged(int i) { } }); } private void updateViewPager(final int i) { updateButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MyActivity.this, i+" Is Selected "+data.size(), Toast.LENGTH_SHORT).show(); data.set(i, "Replaced "+i); myViewPager.getAdapter().notifyDataSetChanged(); } }); } private class MyViewPagerAdapter extends PagerAdapter { private List data; private Context ctx; public MyViewPagerAdapter(Context ctx, List data) { this.ctx = ctx; this.data = data; } @Override public int getCount() { return data.size(); } @Override public int getItemPosition(Object object) { return POSITION_NONE; } @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 restreState(Parcelable arg0, ClassLoader arg1) { } @Override public void startUpdate(View arg0) { } @Override public void finishUpdate(View arg0) { } } } 

I think I’ve made a simple way to notify of data set changes:

First, change a bit the way the instantiateItem function works:

  @Override public Object instantiateItem(final ViewGroup container, final int position) { final View rootView = mInflater.inflate(...,container, false); rootView.setTag(position); updateView(rootView, position); container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mViewPager.setObjectForPosition(rootView, position); return rootView; } 

for “updateView” , fill the view with all the data you wish to fill (setText,setBitmapImage,…) .

verify that destroyView works like this:

  @Override public void destroyItem(final ViewGroup container, final int position, final Object obj) { final View viewToRemove = (View) obj; mViewPager.removeView(viewToRemove); } 

Now, suppose you need to change the data, do it, and then call the next function on the PagerAdapter :

  public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos, final NotifyLocation toPos) { final int offscreenPageLimit = viewPager.getOffscreenPageLimit(); final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit : mSelectedPhotoIndex + offscreenPageLimit; final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit : mSelectedPhotoIndex + offscreenPageLimit; if (fromPosInt <= toPosInt) { notifyDataSetChanged(); for (int i = fromPosInt; i <= toPosInt; ++i) { final View pageView = viewPager.findViewWithTag(i); mPagerAdapter.updateView(pageView, i); } } } public enum NotifyLocation { MOST_LEFT, CENTER, MOST_RIGHT } 

For example if you wish to notify all of the views that are being shown by the viewPager that something has changed, you can call:

 notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT); 

Eso es.

For what it’s worth, on KitKat+ it seems that adapter.notifyDataSetChanged() is enough to cause the new views to show up, provided that you’ve setOffscreenPageLimit sufficiently high. I’m able to get desired behavior by doing viewPager.setOffscreenPageLimit(2) .

I actually use notifyDataSetChanged() on ViewPager and CirclePageIndicator and after that I call destroyDrawingCache() on ViewPager and it works.. None of the other solutions worked for me.

if you want to solve Viewpager update problem check this project, it will solve your problem.

and more understand check that;

happy coding…