¿Actualiza ViewPager dinámicamente?

No puedo actualizar el contenido en ViewPager.

Una pregunta: ¿Cuál es la relación y el uso correcto de los métodos instantiateItem () y getItem () en la clase FragmentPagerAdapter?

Solo estaba usando getItem () para crear instancias y devolver mis fragmentos:

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

Esto funcionó bien (pero como dije no puedo cambiar el contenido).

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

Particular en el que se habla sobre el método instanciateItem ():

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

Entonces ahora quiero implementar instantaniateItem () para hacer eso. Pero no sé qué tengo que devolver allí (return es Object) y cuál es la relación con getItem (int position)?

Actualmente estoy usando getItem para instanciar el Fragmento, ¿está mal? ¿Pero entonces tengo que poner los fragmentos en la variable de instancia, o no implementar getItem () en absoluto …? Simplemente no lo entiendo.

Intenté leer la referencia :

  • público abstracto Fragmento getItem (int position)

    Devuelve el Fragmento asociado a una posición especificada.

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

    Crea la página para la posición dada. El adaptador es responsable de agregar la vista al contenedor que se proporciona aquí, aunque solo debe asegurarse de que esto se realice cuando vuelva de finishUpdate (ViewGroup). Parámetros

    contenedor La vista que contiene la página donde se mostrará. position La posición de la página a ser instanciada.

    Devoluciones

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

… pero todavía no entiendo cómo están relacionados y qué tengo que hacer.

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/android-developers@googlegroups.com/msg200477.html

Al utilizar FragmentPagerAdapter o FragmentStatePagerAdapter, lo mejor es tratar únicamente con getItem() y no tocar instantiateItem() en absoluto. La interfaz instantiateItem()destroyItem()isViewFromObject() en PagerAdapter es una interfaz de nivel inferior que FragmentPagerAdapter utiliza para implementar la interfaz getItem() mucho más simple.

Antes de entrar en esto, debería 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 utilizar FragmentPagerAdapter como ejemplo; eso no funcionará porque FragmentPagerAdapter nunca destruye un fragmento una vez que se ha mostrado por primera vez.

No recomiendo la solución provisional setTag() y findViewWithTag() proporcionada en la publicación que ha vinculado. Como ha descubierto, usar setTag() y findViewWithTag() no funciona con fragmentos, por lo que no es una buena coincidencia.

La solución correcta es anular getItemPosition() . Cuando se notifyDataSetChanged() , ViewPager llama a getItemPosition() en todos los elementos de su adaptador para ver si deben moverse a una posición diferente o eliminarse.

De forma predeterminada, getItemPosition() devuelve POSITION_UNCHANGED , lo que significa, “Este objeto está bien donde está, no lo destruya ni lo elimine”. La devolución de POSITION_NONE soluciona el problema al decir: “Este objeto ya no es un elemento que estoy mostrando, quítelo”. Por lo tanto, tiene el efecto de eliminar y volver a crear 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 reciclaje de vistas. Si implementa esta solución y el rendimiento es satisfactorio, estará listo para las carreras. Trabajo hecho.

Si necesita un mejor rendimiento, puede utilizar una getItemPosition() más getItemPosition() . Aquí hay un ejemplo de un buscapersonas que crea fragmentos de una lista de cadenas:

 ViewPager pager = /* get my ViewPager */; // assume this actually has stuff in it final ArrayList titles = new ArrayList(); 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 nuevos títulos. 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 ha sido recreado, pero necesita ser actualizado de todos modos? Las actualizaciones de un fragmento vivo se manejan mejor con el fragmento en sí. Esa es la ventaja de tener un fragmento, después de todo, es su propio controlador. Un fragmento puede agregar un oyente o un observador a otro objeto en onCreate() y luego eliminarlo en onDestroy() , gestionando así las actualizaciones. No tiene que poner todo el código de actualización dentro de getItem() como lo hace en un adaptador para un ListView u otros tipos de AdapterView.

Una última cosa: el hecho de que FragmentPagerAdapter no destruya un fragmento no significa que getItemPosition sea completamente inútil en un FragmentPagerAdapter. Todavía puede usar esta callback para reordenar sus fragmentos en ViewPager. Sin embargo, nunca los eliminará completamente del FragmentManager.

En lugar de devolver POSITION_NONE desde getItemPosition() y causar recreación de vista completa, haga 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 interfaz UpdateableFragment :

 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); } 

Su clase de datos:

 public class UpdateData { //whatever you want here } 

para aquellos que todavía enfrentan el mismo problema al que me enfrenté antes cuando tengo un ViewPager con 7 fragmentos. el valor predeterminado de estos fragmentos para cargar el contenido en inglés del servicio de API, 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 que ViewPager en la actividad principal actualice los fragmentos para que coincidan con la selección de idioma del usuario y cargar el contenido en árabe si el usuario eligió el árabe aquí lo que hice para trabajar desde la primera vez

1- Debes usar FragmentStatePagerAdapter como se menciona arriba.

2-on mainActivity anulo el onResume e hice lo siguiente

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

3-superé la posición getItemPosition () en mPagerAdapter y la devolvió POSITION_NONE.

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

funciona como el encanto

He encontrado 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 ViewPager de Android y actualice lo que hago. Estoy usando FragmentStatePagerAdapter en API nivel 17 y actualmente tengo solo 2 fragmentos. Creo que debe haber algo incorrecto, corrígeme, gracias. enter image description here

  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 tu código. Si está usando un CursorLoader , se carga automáticamente ya que hay un observador de datos registrado.

  2. Después de llamar a viewpager.setAdapter(pageradapter) , viewpager.setAdapter(pageradapter) del adaptador se llama constantemente para crear fragmentos. Entonces, si se cargan datos, getCount() puede devolver 0, por lo tanto, no es necesario crear fragmentos ficticios para los datos que se muestran.

  3. Después de que se carguen los datos, el adaptador no generará fragmentos automáticamente ya que getCount() sigue siendo 0, por lo que podemos establecer el número de datos realmente cargado que devuelve getCount() , luego llamar al notifyDataSetChanged() del adaptador. ViewPager comienza a crear fragmentos (solo los primeros 2 fragmentos) por datos en la memoria. Se hace antes de que se notifyDataSetChanged() . Entonces ViewPager tiene los fragmentos correctos que necesita.

  4. Si los datos en la base de datos y la memoria se actualizan (escritura), o solo los datos en la memoria se actualizan (escriben de nuevo), o solo se actualizan los datos en la base de datos. En los dos últimos casos, si los datos no se cargan automáticamente de la base de datos a la memoria (como se mencionó anteriormente). El adaptador ViewPager y pager solo trata los datos en la memoria.

  5. Entonces, cuando los datos en la memoria se actualizan, solo necesitamos llamar al notifyDataSetChanged() del adaptador. Como el fragmento ya está creado, se onItemPosition() del adaptador antes de que notifyDataSetChanged() regrese. No es necesario hacer getItemPosition() en getItemPosition() . A continuación, se actualizan los datos.

Pruebe destroyDrawingCache() en ViewPager después de notifyDataSetChanged() en su código.

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.

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

 private class MyAdapter extends FragmentStatePagerAdapter { // [Rest of implementation] @Override public void restreState(Parcelable state, ClassLoader loader) {} } 

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

 private class PagerAdapter extends FragmentStatePagerAdapter{ Bundle oBundle; FragmentManager oFragmentManager; ArrayList 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; } } 

de modo que getItemPosition() devuelve POSITION_NONE solo para los fragmentos que se encuentran actualmente en FragmentManager cuando se llama a getItemPosition . (Tenga en cuenta que este FragmentStatePager y el ViewPager asociado con él están contenidos en un Fragmento que no está en una Actividad)

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

Guardo referencias a los fragmentos creados en getItem en una matriz. Esto funciona bien siempre que la actividad no se destruya debido a la configuraciónCambio o falta de memoria o lo que sea (-> al volver a la actividad, los fragmentos vuelven a su estado anterior sin que se vuelva a llamar a ‘getItem’ y así 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 me funcione y quisiera compartirla contigo, 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 así? Hasta ahora me funciona muy bien …

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% 20Stars & groupby = & 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 … es posible que una cumpla con sus requisitos.

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

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

Asegúrese de que su adaptador debe extender FragmentStatePagerAdapter

Utilizo la biblioteca de EventBus para actualizar el contenido de Fragment en ViewPager . La lógica es simple, al igual que el documento de EventBus cómo hacerlo . No es necesario controlar la instancia de FragmentPagerAdapter . El código está aquí:

1: definir eventos

Definir qué mensaje es necesario actualizar.

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

2. Preparar suscriptores

Escriba el código a continuación 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.Post eventos

Escriba el código siguiente en otra Activity u otro Fragment que necesite actualizar el parámetro

 //input update message EventBus.getDefault().post(new UpdateCountEvent(count)); 

Había intentado tantos enfoques diferentes, ninguno realmente resolvió 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” de entero público, que puede decirme si es la página que quiero actualizar.

 public class FragmentsMain extends Fragment { private Cursor cursor; private static Context context; public int page=-1; 

Sé que llego tarde a la fiesta. TabLayout#setupWithViewPager(myViewPager); el problema llamando a TabLayout#setupWithViewPager(myViewPager); justo después de FragmentPagerAdapter#notifyDataSetChanged();

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

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

 private void updateFragment(Item item) { List 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 los métodos en esos fragmentos y determinar si es el fragmento que desea actualizar.

Esto podría ser útil para alguien: en mi caso, cuando insertaba una nueva página, el buscapersonas buscaba la posición de un fragmento existente dos veces, pero no preguntaba por 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 un control de cordura a destroyItem () para evitar lockings:

     if (position < mFragments.size()) { mFragments.set(position, null); } 

He revisado todas las respuestas anteriores y varias otras, pero todavía no he podido encontrar algo que me sirva (con diferentes tipos de fragmentos junto con la adición y eliminación dinámicas de tabs). El siguiente enfoque 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 titles = new ArrayList<>(); private Map 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); } } 

Utilice FragmentStatePagerAdapter en lugar de FragmentPagerAdapter si desea recrear o volver a cargar fragmentos en base a índices. Por ejemplo, si desea volver a cargar un fragmento que no sea FirstFragment, puede verificar la instancia y devolver una posición como esta

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