without with viewpager tablayout studio fragments example android android-fragments android-viewpager

android - viewpager - tablayout without fragments



Actualizar ViewPager dinĂ¡micamente? (18)

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

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 para su ejemplo, que no funcionará porque FragmentPagerAdapter nunca destruye un fragmento después de que se muestra la primera vez.

No recomiendo la setTag() y findViewWithTag() proporcionada en la publicación que vinculó. 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 se llama a getItemPosition() , ViewPager invoca getItemPosition() en todos los elementos de su adaptador para ver si es necesario getItemPosition() a una posición diferente o eliminarlos.

De forma predeterminada, getItemPosition() devuelve POSITION_UNCHANGED , lo que significa que "este objeto está bien donde está, no lo destruya ni lo elimine". Al POSITION_NONE soluciona el problema diciendo: "Este objeto ya no es un elemento que estoy mostrando, elimínelo". Por lo tanto, tiene el efecto de eliminar y volver a crear cada elemento en 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 implementas esta corrección y el rendimiento es satisfactorio, estás listo para las carreras. Trabajo hecho.

Si necesita un mejor rendimiento, puede usar una getItemPosition() más sofisticada de getItemPosition() . Aquí hay un ejemplo para un paginador que crea fragmentos fuera 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 nuevos títulos. Cualquier fragmento que muestre títulos que aún 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é sucede si el fragmento no se ha recreado, pero debe actualizarse de todos modos? Las actualizaciones de un fragmento vivo se manejan mejor mediante el propio fragmento. 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() , administrando 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 otro tipo de AdapterView.

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

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 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 (pero como dije no puedo cambiar el contenido).

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

Particularmente uno en el que se ha hablado del método instantiateItem ():

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

Así que ahora quiero implementar instantiateItem () para hacer eso. Pero no sé qué tengo que devolver allí (devolver es Objeto) y cuál es la relación con getItem (posición int)?

Actualmente estoy usando getItem para crear una instancia del 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 reference :

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

    Devuelve el Fragmento asociado a una posición especificada.

  • Objeto público instantiateItem (ViewGroup container, int position)

    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 haga antes de que regrese de finishUpdate (ViewGroup). Parámetros

    contenedor La vista contenedora en la que se mostrará la página. posición La posición de la página para 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/[email protected]/msg200477.html


Después de horas de frustración mientras intentaba todas las soluciones anteriores para superar este problema y también probé muchas soluciones en otras preguntas similares como this , this y this que FALLÓ conmigo para resolver este problema y hacer que ViewPager destruya el Fragment antiguo y lo rellene El pager con el nuevo Fragment s. He resuelto el problema de la siguiente manera:

1) Haga que la clase ViewPager amplíe FragmentPagerAdapter siguiente manera:

public class myPagerAdapter extends FragmentPagerAdapter {

2) Cree un elemento para el 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 del ViewPager tome mi instancia de FragmentManager para almacenarlo en mi class siguiente manera:

private FragmentManager mFragmentManager; private ArrayList<PagerItem> mPagerItems; public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> 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 fragmentManager directamente para hacer que el adapter establezca el nuevo fragment de la nueva lista 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 Activity o Fragment no reinicialice 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<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.


En lugar de devolver POSITION_NONE de getItemPosition() y provocar una 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 }


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 su uso 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 los métodos en esos fragmentos para determinar si es el fragmento que desea actualizar.


Esto podría ser de ayuda para alguien: en mi caso, al insertar una nueva página, el paginador de la vista pedía la posición de un fragmento existente dos veces, pero no la posición del nuevo elemento, lo que provocaba 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. Reemplazar NotifyDataSetChanged ()

    @Override public void notifyDataSetChanged() { mFragments.clear(); super.notifyDataSetChanged(); }

  3. Agregue una verificación de cordura para destroyItem () para evitar bloqueos:

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


Había estado probando tantos enfoques diferentes, ninguno realmente resolvía 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 enteros públicos, 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;


He revisado todas las respuestas anteriores y muchas otras publicaciones, pero aún no pude encontrar algo que me funcionara (con diferentes tipos de fragmentos, además de agregar y eliminar pestañas de forma dinámica). El siguiente enfoque de FWIW es lo que me funcionó (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); } }


He vivido el mismo problema y he buscado demasiadas veces. Cualquier respuesta dada en o vía google no fue una 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 los cambios en la vista de la vista. Mi solución final fue muy fácil de usar para cualquiera. Cuando se agrega un nuevo elemento a la lista y se quiere actualizar la lista. En primer lugar, establezca el adaptador de viewpager en nulo, luego vuelva a crear el adaptador y establezca i en él para viewpager.

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

Asegúrese de que su adaptador debe extender FragmentStatePagerAdapter


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


Me encontré con 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 el ViewPager de Android y actualice como lo hago. Estoy usando FragmentStatePagerAdapter en el nivel de API 17 y actualmente solo tengo 2 fragmentos. Creo que debe haber algo que no sea correcto, corríjame, gracias.

  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 a viewpager.setAdapter(pageradapter) , se llama constantemente a viewpager.setAdapter(pageradapter) del adaptador para construir fragmentos. Por lo tanto, si se están cargando datos, getCount() puede devolver 0, por lo que no es necesario crear fragmentos ficticios para que no se muestren datos.

  3. Después de que se carguen los datos, el adaptador no generará fragmentos automáticamente ya que getCount() aún es 0, por lo que podemos configurar el número de datos realmente cargados para que devuelva getCount() , luego llamar a notifyDataSetChanged() del adaptador. ViewPager comienza a crear fragmentos (solo los primeros 2 fragmentos) por datos en la memoria. Se hace antes de notifyDataSetChanged() . Entonces el ViewPager tiene 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 (respuesta), 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 ViewPager y el adaptador de buscapersonas solo tratan con los datos en la memoria.

  5. Por lo tanto, cuando se actualizan los datos en la memoria, solo tenemos que llamar a notifyDataSetChanged() del adaptador. Dado que el fragmento ya está creado, se onItemPosition() del adaptador antes de notifyDataSetChanged() devuelve. No es necesario hacer getItemPosition() en getItemPosition() . Luego, los datos se actualizan.


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

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


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


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



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:

Guardo referencias a los fragmentos creados en getItem en una matriz. Esto funciona bien siempre y cuando la actividad no se destruya debido a la configuración Cambio 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í, de esta manera:

@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 contigo, pero también quería preguntarte si alguien más intentó algo similar y si hay alguna razón por la que no debería hacerlo. hazlo así? Hasta ahora me funciona muy bien ...


Utilice FragmentStatePagerAdapter en lugar de FragmentPagerAdapter si desea recrear o recargar el fragmento en base al índice. Por ejemplo, si desea recargar otro 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; }


para aquellos que todavía enfrentan el mismo problema que enfrenté antes cuando tengo un ViewPager con 7 fragmentos. el valor predeterminado para estos fragmentos para cargar el contenido en inglés desde el 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 cargue el contenido en árabe si el usuario eligió árabe aquí, lo que hice para trabajar desde la primera vez

1- Debe usar FragmentStatePagerAdapter como se mencionó anteriormente.

2-en mainActivity i invalido el onResume e hice lo siguiente

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

3-i reemplazó a getItemPosition () en mPagerAdapter y hace que devuelva POSITION_NONE.

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

funciona como un encanto


EventBus biblioteca 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

Defina qué mensaje es necesario actualizar.

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

2.Preparar a los suscriptores

Escriba el siguiente 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.Post eventos

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

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