Pregunta

Estoy usando el V4 Compatibilidad ViewPager en Android. Mi FragmentActivity tiene un montón de datos que se mostrarán de diferentes maneras en diferentes páginas en mi ViewPager. Hasta ahora solo tengo 3 instancias del mismo ListFragment, pero en el futuro tendré 3 instancias de diferentes fragmentos de Lists. El ViewPager está en una pantalla de teléfono vertical, las listas no están una al lado de la otra.

Ahora, un botón en el Fragment de List comienza una actividad de página completa separada (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 puede ver, mi primer intento fue notificar a DataSetchanged en todo el FragmentPagerAdapter, y esto mostró actualizar los datos a veces, pero otros obtuve una explicación ilegal: no puedo realizar esta acción después de OnsaveInstancate.

Mi segundo intento implicó tratar de llamar a una función de actualización en mi ListFragment, pero GetID en GetItem devolvió 0. Según los documentos que probé por

Adquisición de una referencia al fragmento de FragmentManager, utilizando FindFragmentById () o FindFragmentBytag ()

¡Pero no conozco la etiqueta o la identificación de mis fragmentos! Tengo un android: id = "@+id/viewpager" para ViewPager, y un android: id = "@android: id/list" para mi lista de listview en el diseño de listfragment, pero no creo que estos sean útiles.

Entonces, ¿cómo puedo? O preferiblemente, b) Llame a una función en cada Fragment de List Afected para actualizar el ListView manualmente.

¡Cualquier ayuda sería aceptada con agradecimiento!

¿Fue útil?

Solución

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

Otros consejos

La respuesta de Barkside funciona con FragmentPagerAdapter Pero no funciona con FragmentStatePagerAdapter, porque no establece etiquetas en fragmentos que pasa a FragmentManager.

Con FragmentStatePagerAdapter Parece que podemos superarlo, usando su instantiateItem(ViewGroup container, int position) llamar. Devuelve referencia al fragmento en la posición position. Si FragmentStatePagerAdapter ya tiene referencia al fragmento en cuestión, instantiateItem Solo devuelve la referencia a ese fragmento y no llama getItem() para instanciarlo de nuevo.

Entonces, supongamos, actualmente estoy mirando Fragment #50 y quiero acceder a Fragment #49. Como están cerca, hay una buena posibilidad de que el #49 ya esté instanciado. 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 compartiré para el beneficio de los demás. La etiqueta de fragmentos dentro de un viewPager está en el formulario "android:switcher:VIEWPAGER_ID:INDEX", dónde VIEWPAGER_ID es el R.id.viewpager En el diseño XML, y el índice es la posición en el ViewPager. Entonces, si se conoce la posición (por ejemplo, 0), puedo actuar 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 funcionará hasta que se sugiera algo mejor.

Si me pregunta, la segunda solución en la página a continuación, que realiza un seguimiento de todas las páginas de fragmentos "activos", es mejor: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

La respuesta de Barkside es demasiado hacky para mí.

Hace un seguimiento de todas las páginas de fragmentos "activos". En este caso, realiza un seguimiento de las páginas de fragmentos en el FragmentStatePagerAdapter, que el ViewPager utiliza.

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 fragmentos "inactivas", necesitamos implementar el método Destroitem (...) 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 actualmente, luego llama:

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

... donde el método GetFragment (int) de Myadapter se ve así:

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

"

De acuerdo, después de probar el método de @barkside anterior, no pude hacer que funcionara con mi aplicación. Entonces recordé que el Aplicación iosched2012 usa un viewpager Además, y ahí es donde encontré mi solución. No utiliza ninguna identificación o etiqueta de fragmento, ya que no están almacenadas por viewpager de una manera fácilmente accesible.

Aquí esta la partes importantes de la Homebreactividad de Aplicaciones Iosched. Prestar especial atención al comentario, como ahí se encuentra la llave.:

@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 almacenar instancias de ti Fragments en el FragmentPagerAdapter al igual que:

    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, recuerda proteger tu Fragment llamadas como así:

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

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

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 extiéndalo, anule este método abstracto, no necesita tener miedo al cambio de grupo de Android

FragmentPageAdapter código fuente 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 de 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 OnCreateView de Fragment ()::

...
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 método para devolver el 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 anular setPrimaryItem método de FragmentPagerAdapter al igual que:

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

}

Luego, para obtener el fragmento actual, lo que hago es:

int index = view_pager.getCurrentItem();
Fragment currentFragment = adapter.getItem(index);

Esta es mi solución, ya que no necesito realizar un seguimiento de mis pestañas y necesito actualizarlas todos de todos modos.

    Bundle b = new Bundle();
    b.putInt(Constants.SharedPreferenceKeys.NUM_QUERY_DAYS,numQueryDays);
    for(android.support.v4.app.Fragment f:getSupportFragmentManager().getFragments()){
        if(f instanceof HomeTermFragment){
            ((HomeTermFragment) f).restartLoader(b);
        }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top