Actualizar datos en ListFragment como parte de ViewPager

Estoy utilizando la compatibilidad v4 ViewPager en Android. My FragmentActivity tiene un montón de datos que se mostrarán de diferentes maneras en diferentes páginas de mi ViewPager. Hasta ahora sólo tengo 3 instancias del mismo ListFragment, pero en el futuro tendré 3 instancias de ListFragments diferentes. El ViewPager está en una pantalla vertical del teléfono, las listas no están de lado a lado.

Ahora un botón en el ListFragment inicia una actividad independiente de página completa (a través de FragmentActivity), que devuelve y FragmentActivity modifica los datos, lo guarda y luego intenta actualizar todas las vistas en su ViewPager. Es aquí donde estoy atrapado.

public class ProgressMainActivity extends FragmentActivity { MyAdapter mAdapter; ViewPager mPager; @Override public void onCreate(Bundle savedInstanceState) { ... mAdapter = new MyAdapter(getSupportFragmentManager()); mPager = (ViewPager) findViewById(R.id.viewpager); mPager.setAdapter(mAdapter); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { ... updateFragments(); ... } public void updateFragments() { //Attempt 1: //mAdapter.notifyDataSetChanged(); //mPager.setAdapter(mAdapter); //Attempt 2: //HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentById(mAdapter.fragId[0]); //fragment.updateDisplay(); } public static class MyAdapter extends FragmentPagerAdapter implements TitleProvider { int[] fragId = {0,0,0,0,0}; public MyAdapter(FragmentManager fm) { super(fm); } @Override public String getTitle(int position){ return titles[position]; } @Override public int getCount(){ return titles.length; } @Override public Fragment getItem(int position) { Fragment frag = HomeListFragment.newInstance(position); //Attempt 2: //fragId[position] = frag.getId(); return frag; } @Override public int getItemPosition(Object object) { return POSITION_NONE; //To make notifyDataSetChanged() do something } } public class HomeListFragment extends ListFragment { ... public static HomeListFragment newInstance(int num) { HomeListFragment f = new HomeListFragment(); ... return f; } ... 

Ahora como puedes ver, mi primer intento fue notifyDataSetChanged en todo el FragmentPagerAdapter, y esto mostró actualizar los datos a veces, pero otros recibí una IllegalStateException: No se puede realizar esta acción después de onSaveInstanceState.

Mi segundo intento involed tratando de llamar a una función de actualización en mi ListFragment, pero getId en getItem devuelto 0. Como por los documentos que intenté por

Adquiriendo una referencia al Fragmento de FragmentManager, usando findFragmentById () o findFragmentByTag ()

Pero no sé la etiqueta o id de mis Fragmentos! Tengo un android: id = "@ + id / viewpager" para ViewPager, y un android: id = "@ android: id / list" para mi ListView en el diseño ListFragment, pero no creo que sean útiles.

Así que, ¿cómo puedo: a) actualizar todo el ViewPager de forma segura de una sola vez (idealmente devolver al usuario a la página en la que estaba antes) – está bien que el usuario vea el cambio de vista. O preferiblemente, b) llamar a una función en cada ListFragment afectado para actualizar el ListView manualmente.

Cualquier ayuda sería aceptada con gratitud!

9 Solutions collect form web for “Actualizar datos en ListFragment como parte de ViewPager”

Intente registrar la etiqueta cada vez que se instancia un fragmento.

 public class MPagerAdapter extends FragmentPagerAdapter { private Map<Integer, String> mFragmentTags; private FragmentManager mFragmentManager; public MPagerAdapter(FragmentManager fm) { super(fm); mFragmentManager = fm; mFragmentTags = new HashMap<Integer, String>(); } @Override public int getCount() { return 10; } @Override public Fragment getItem(int position) { return Fragment.instantiate(mContext, AFragment.class.getName(), null); } @Override public Object instantiateItem(ViewGroup container, int position) { Object obj = super.instantiateItem(container, position); if (obj instanceof Fragment) { // record the fragment tag here. Fragment f = (Fragment) obj; String tag = f.getTag(); mFragmentTags.put(position, tag); } return obj; } public Fragment getFragment(int position) { String tag = mFragmentTags.get(position); if (tag == null) return null; return mFragmentManager.findFragmentByTag(tag); } } 

La respuesta de Barkside funciona con FragmentPagerAdapter pero no funciona con FragmentStatePagerAdapter , ya que no establece etiquetas en los fragmentos que pasa a FragmentManager .

Con FragmentStatePagerAdapter parece que podemos obtener, mediante su instantiateItem(ViewGroup container, int position) . Devuelve la referencia al fragmento en la posición de position . Si FragmentStatePagerAdapter ya contiene referencia al fragmento en cuestión, instantiateItem simplemente devuelve la referencia a ese fragmento, y no llama a getItem() para instanciarlo de nuevo.

Por lo tanto, supongamos que estoy buscando el fragmento # 50, y quiero acceder al fragmento # 49. Ya que están cerca, hay una buena probabilidad de que el # 49 ya se instanciará. Asi que,

 ViewPager pager = findViewById(R.id.viewpager); FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter(); MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49) 

OK, creo que he encontrado una manera de realizar la solicitud b) en mi propia pregunta, así que voy a compartir para beneficio de los demás. La etiqueta de fragmentos dentro de un ViewPager se encuentra en la forma "android:switcher:VIEWPAGER_ID:INDEX" , donde VIEWPAGER_ID es el R.id.viewpager en el diseño XML, e INDEX es la posición en el viewpager. Así que si la posición es conocida (por ejemplo, 0), puedo realizar en updateFragments() :

  HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentByTag( "android:switcher:"+R.id.viewpager+":0"); if(fragment != null) // could be null if not instantiated yet { if(fragment.getView() != null) { // no need to call if fragment's onDestroyView() //has since been called. fragment.updateDisplay(); // do what updates are required } } 

No tengo idea de si esta es una forma válida de hacerlo, pero lo hará hasta que se sugiera algo mejor.

Si me preguntas, la segunda solución en la página siguiente, manteniendo un registro de todas las páginas de fragmento "activo", es mejor: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part -ii.html

La respuesta de barkside es demasiado hacky para mí.

Usted hace un seguimiento de todas las páginas de fragmento "activo". En este caso, realiza un seguimiento de las páginas fragmento en el FragmentStatePagerAdapter, que es utilizado por ViewPager.

 private final SparseArray<Fragment> mPageReferences = new SparseArray<Fragment>(); public Fragment getItem(int index) { Fragment myFragment = MyFragment.newInstance(); mPageReferences.put(index, myFragment); return myFragment; } 

Para evitar mantener una referencia a las páginas de fragmento "inactivas", necesitamos implementar el método destroyItem (…) de FragmentStatePagerAdapter:

 public void destroyItem(View container, int position, Object object) { super.destroyItem(container, position, object); mPageReferences.remove(position); } 

… y cuando necesite acceder a la página visible en ese momento, debe llamar a:

 int index = mViewPager.getCurrentItem(); MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter()); MyFragment fragment = adapter.getFragment(index); 

… donde el método getFragment (int) de MyAdapter tiene este aspecto:

 public MyFragment getFragment(int key) { return mPageReferences.get(key); } 

"

Bueno, después de probar el método por @barkside anterior, no pude conseguir que funcione con mi aplicación. Entonces recordé que la aplicación IOSched2012 también usa un viewpager , y ahí es donde encontré mi solución. No utiliza ningún identificador de fragmento o etiquetas, ya que éstas no se almacenan por viewpager de una manera fácilmente accesible.

Aquí están las partes importantes de las aplicaciones de IOSched HomeActivity. Preste especial atención al comentario , ya que ahí está la clave .:

 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // Since the pager fragments don't have known tags or IDs, the only way to persist the // reference is to use putFragment/getFragment. Remember, we're not persisting the exact // Fragment instance. This mechanism simply gives us a way to persist access to the // 'current' fragment instance for the given fragment (which changes across orientation // changes). // // The outcome of all this is that the "Refresh" menu button refreshes the stream across // orientation changes. if (mSocialStreamFragment != null) { getSupportFragmentManager().putFragment(outState, "stream_fragment", mSocialStreamFragment); } } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (mSocialStreamFragment == null) { mSocialStreamFragment = (SocialStreamFragment) getSupportFragmentManager() .getFragment(savedInstanceState, "stream_fragment"); } } 

Y almacena instancias de ti Fragments en el FragmentPagerAdapter como así:

  private class HomePagerAdapter extends FragmentPagerAdapter { public HomePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return (mMyScheduleFragment = new MyScheduleFragment()); case 1: return (mExploreFragment = new ExploreFragment()); case 2: return (mSocialStreamFragment = new SocialStreamFragment()); } return null; } 

Además, recuerde guardar sus llamadas Fragment así:

  if (mSocialStreamFragment != null) { mSocialStreamFragment.refresh(); } 

Puede copiar FragmentPagerAdapter y modificar algún código fuente, agregar el método getTag()

por ejemplo

 public abstract class AppFragmentPagerAdapter extends PagerAdapter { private static final String TAG = "FragmentPagerAdapter"; private static final boolean DEBUG = false; private final FragmentManager mFragmentManager; private FragmentTransaction mCurTransaction = null; private Fragment mCurrentPrimaryItem = null; public AppFragmentPagerAdapter(FragmentManager fm) { mFragmentManager = fm; } public abstract Fragment getItem(int position); @Override public void startUpdate(ViewGroup container) { } @Override public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); String name = getTag(position); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, getTag(position)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment) object).getView()); mCurTransaction.detach((Fragment) object); } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (fragment != mCurrentPrimaryItem) { if (mCurrentPrimaryItem != null) { mCurrentPrimaryItem.setMenuVisibility(false); mCurrentPrimaryItem.setUserVisibleHint(false); } if (fragment != null) { fragment.setMenuVisibility(true); fragment.setUserVisibleHint(true); } mCurrentPrimaryItem = fragment; } } @Override public void finishUpdate(ViewGroup container) { if (mCurTransaction != null) { mCurTransaction.commitAllowingStateLoss(); mCurTransaction = null; mFragmentManager.executePendingTransactions(); } } @Override public boolean isViewFromObject(View view, Object object) { return ((Fragment) object).getView() == view; } @Override public Parcelable saveState() { return null; } @Override public void restoreState(Parcelable state, ClassLoader loader) { } public long getItemId(int position) { return position; } private static String makeFragmentName(int viewId, long id) { return "android:switcher:" + viewId + ":" + id; } protected abstract String getTag(int position); } 

Luego extenderlo, anular este método abstracto, no es necesario tener miedo de Android Group cambio

Código fuente FragmentPageAdapter en el futuro

  class TimeLinePagerAdapter extends AppFragmentPagerAdapter { List<Fragment> list = new ArrayList<Fragment>(); public TimeLinePagerAdapter(FragmentManager fm) { super(fm); list.add(new FriendsTimeLineFragment()); list.add(new MentionsTimeLineFragment()); list.add(new CommentsTimeLineFragment()); } public Fragment getItem(int position) { return list.get(position); } @Override protected String getTag(int position) { List<String> tagList = new ArrayList<String>(); tagList.add(FriendsTimeLineFragment.class.getName()); tagList.add(MentionsTimeLineFragment.class.getName()); tagList.add(CommentsTimeLineFragment.class.getName()); return tagList.get(position); } @Override public int getCount() { return list.size(); } } 

También funciona sin problemas:

En algún lugar del diseño del fragmento de página:

 <FrameLayout android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone" android:id="@+id/fragment_reference"> <View android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/> </FrameLayout> 

En el fragmento onCreateView ():

 ... View root = inflater.inflate(R.layout.fragment_page, container, false); ViewGroup ref = (ViewGroup)root.findViewById(R.id.fragment_reference); ref.setTag(this); ref.getChildAt(0).setTag("fragment:" + pageIndex); return root; 

Y el método para devolver fragmento de ViewPager, si existe:

 public Fragment getFragment(int pageIndex) { View w = mViewPager.findViewWithTag("fragment:" + pageIndex); if (w == null) return null; View r = (View) w.getParent(); return (Fragment) r.getTag(); } 

Alternativamente, puede sobrescribir el método setPrimaryItem desde FragmentPagerAdapter manera:

 public void setPrimaryItem(ViewGroup container, int position, Object object) { if (mCurrentFragment != object) { mCurrentFragment = (Fragment) object; //Keep reference to object ((MyInterface)mCurrentFragment).viewDidAppear();//Or call a method on the fragment } super.setPrimaryItem(container, position, object); } public Fragment getCurrentFragment(){ return mCurrentFragment; } 

Quiero dar mi enfoque en caso de que pueda ayudar a alguien más:

Este es mi adaptador de buscapersonas:

  public class CustomPagerAdapter extends FragmentStatePagerAdapter{ private Fragment[] fragments; public CustomPagerAdapter(FragmentManager fm) { super(fm); fragments = new Fragment[]{ new FragmentA(), new FragmentB() }; } @Override public Fragment getItem(int arg0) { return fragments[arg0]; } @Override public int getCount() { return fragments.length; } } 

En mi actividad tengo:

 public class MainActivity { private ViewPager view_pager; private CustomPagerAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = new CustomPagerAdapter(getSupportFragmentManager()); view_pager = (ViewPager) findViewById(R.id.pager); view_pager.setAdapter(adapter); view_pager.setOnPageChangeListener(this); ... } } 

Entonces para conseguir el fragmento actual lo que hago es:

 int index = view_pager.getCurrentItem(); Fragment currentFragment = adapter.getItem(index); 
  • Cómo hacer deslizar el deslizador en un paginador de vista en android
  • Cómo agregar un fragmento dentro de un ViewPager usando Nested Fragment (Android 4.2)
  • Flip imágenes en un viewpager
  • Deshabilitar los gestos en PagerAdapter / ViewPager
  • Lazy cargando ViewPagers en un ScrollView
  • Sincronizar todo el ListView en un ViewPager
  • Cómo forzar a ViewPager a volver a instanciar sus elementos
  • Viewpager con altura dinámica no funciona (siempre use la altura del primer fragmento)
  • Cambio de orientación con viewPager dentro de FragmentActivity
  • Tener páginas de desplazamiento vertical en ViewPager
  • ViewPager en TabFragment no se carga por segunda vez
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.