Estoy tratando de agregar dinámicamente y quitar los Fragmentos de un ViewPager, añadiendo funciona sin problemas, pero la eliminación no funciona como se esperaba.

Cada vez que desea eliminar el elemento actual, la última de ellas se elimina.

También he probado a utilizar un FragmentStatePagerAdapter o retorno POSITION_NONE en el adaptador del getItemPosition método.

¿Qué estoy haciendo mal?

Aquí tenemos un ejemplo:

MainActivity.java

public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);
mAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
}
private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}
@Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
@Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
@Override
public int getCount() {
return mProvider.getCount();
}
}
}

TextProvider.java

public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}

MyFragment.java

public class MyFragment extends Fragment {
private String mText;
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}
public MyFragment() {
}
public MyFragment(String text) {
this.mText = text;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment, container, false);
((TextView) root.findViewById(R.id.position)).setText(mText);
return root;
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />
<Button
android:id="@+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>

fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />
</LinearLayout>
  • +1 para el código fuente
  • es correcto el uso de la no-constructor predeterminado en MyFragment.java??
InformationsquelleAutor astuetz | 2012-05-01

15 Comentarios

  1. 91

    La solución de Louth no fue suficiente para conseguir que las cosas funcionen para mí, como la existente fragmentos no fueron destruidos. Motivados por esta respuesta, me encontré con que la solución es reemplazar el getItemId(int position) método de FragmentPagerAdapter para dar un nuevo IDENTIFICADOR único cada vez que ha habido un cambio en la posición esperada de un Fragmento.

    Código Fuente:

    private class MyPagerAdapter extends FragmentPagerAdapter {
    private TextProvider mProvider;
    private long baseId = 0;
    public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
    super(fm);
    this.mProvider = provider;
    }
    @Override
    public Fragment getItem(int position) {
    return MyFragment.newInstance(mProvider.getTextForPosition(position));
    }
    @Override
    public int getCount() {
    return mProvider.getCount();
    }
    //this is called when notifyDataSetChanged() is called
    @Override
    public int getItemPosition(Object object) {
    //refresh all fragments when data set changed
    return PagerAdapter.POSITION_NONE;
    }
    @Override
    public long getItemId(int position) {
    //give an ID different from position when position has been changed
    return baseId + position;
    }
    /**
    * Notify that the position of a fragment has been changed.
    * Create a new ID for each position to force recreation of the fragment
    * @param n number of items which have been changed
    */
    public void notifyChangeInPosition(int n) {
    //shift the ID returned by getItemId outside the range of all previous fragments
    baseId += getCount() + n;
    }
    }

    Ahora, por ejemplo, si elimina una sola ficha o hacer algún tipo de cambio a la orden, usted debe llamar a notifyChangeInPosition(1) antes de llamar a notifyDataSetChanged(), que se asegurará de que todos los Fragmentos se volverá a crear.

    Por qué esta solución funciona

    Primordial getItemPosition():

    Cuando notifyDataSetChanged() se llama, el adaptador de llamadas de la notifyChanged() método de la ViewPager que se adjunta. El ViewPager, a continuación, comprueba el valor devuelto por el adaptador del getItemPosition() para cada elemento, la eliminación de aquellos elementos que volver POSITION_NONE (véase el código fuente) y, a continuación, la repoblación.

    Primordial getItemId():

    Esto es necesario para evitar el adaptador de recarga del antiguo fragmento cuando el ViewPager es de repoblación. Se puede comprender fácilmente por qué esto funciona mirando el código fuente para instantiateItem() en FragmentPagerAdapter.

        final long itemId = getItemId(position);
    //Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    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,
    makeFragmentName(container.getId(), itemId));
    }

    Como se puede ver, la getItem() método se llama sólo si el fragmento manager no encuentra existentes fragmentos con el mismo Id. A mí me parece un error que los fragmentos están todavía conectados, incluso después de notifyDataSetChanged() se llama, pero la documentación de ViewPager hace constar claramente que:

    Nota: esta clase está actualmente bajo principios de diseño y desarrollo. El API es probable que cambie en las actualizaciones posteriores de la biblioteca de compatibilidad, lo que requiere cambios en el código fuente de las aplicaciones compiladas para la versión más reciente.

    Así que espero que la solución que aquí no será necesario en un futuro la versión de la biblioteca de compatibilidad.

    • Muchas gracias por esta respuesta! Ha sido agotador, tratando de resolver este problema sin usar FragmentStatePagerAdapter. Gran explicación demasiado. Has encontrado una manera de conseguir getItem a activar cuando volviendo al fragmento? He intentado usar su inteligente notifyDataSetChanged() en varios lugares (es decir, onStop) sin éxito
    • Yo no entiendo muy bien tu pregunta… por Favor enviar una pregunta nueva, incluyendo el código fuente si usted no puede averiguar.
    • Esto funciona como un encanto!
    • 2016, esta solución sigue siendo necesaria para hacer notifyDataSetChanged() a trabajar.
    • Wow! Increíble explicación. Tuve una muy complejo viewpager donde la puedo quitar y de la inserción de elementos dinámicamente y no consigo que funcione sin la comprensión de todo esto.
    • Todavía necesita esta solución alternativa para sustituir la antigua fragmento. Qué diablos.
    • Yo estaba usando otra solución que ha sido arrestado en la biblioteca de soporte de 25, pero este está trabajando como un encanto. Yo solo deseo darte más de un upvote. Gracias, compañero!
    • Gracias bro. Funciona muy bien.
    • 2017 ahora. Y todavía quiero dar las gracias por esta solución.
    • es esta solucionado en cualquier otra forma en 2018? O necesitamos soluciones?
    • la solución más simple stackoverflow.com/a/52892717/3663254
    • puede usted por favor me dan el código fuente porque por encima de enlace no se abre

  2. 281

    El ViewPager no quitar fragmentos con el código anterior, ya que las cargas de varios puntos de vista (o fragmentos en su caso) en la memoria. Además de la visible de la vista, también carga la vista a ambos lados de la visible. Esto proporciona el desplazamiento suave de la vista para ver que hace el ViewPager tan fresco.

    Para lograr el efecto que desea, usted necesita para hacer un par de cosas.

    1. Cambiar el FragmentPagerAdapter a un FragmentStatePagerAdapter. La razón de esto es que el FragmentPagerAdapter mantendrá todos los puntos de vista que se carga en la memoria para siempre. Donde el FragmentStatePagerAdapter dispone de vistas que caen fuera de la corriente y transitable vistas.

    2. Reemplazar el adaptador método getItemPosition (que se muestra a continuación). Cuando llamamos a mAdapter.notifyDataSetChanged(); el ViewPager interroga el adaptador para determinar qué ha cambiado en términos de posicionamiento. Utilizamos este método para decir que todo ha cambiado para volver a procesar a todos su punto de vista de posicionamiento.

    Y aquí está el código…

    private class MyPagerAdapter extends FragmentStatePagerAdapter {
    //... your existing code
    @Override
    public int getItemPosition(Object object){
    return PagerAdapter.POSITION_NONE;
    }
    }
    • Gracias por esto!
    • Tan útil! Gracias!
    • Ok, esta parece ser la solución más sencilla para este problema. Aunque un par de otras soluciones se discuten en los informes de error aquí : code.google.com/p/android/issues/detail?id=19110 y aquí : code.google.com/p/android/issues/detail?id=19001
    • Yo estoy usando el ViewPager con TabsAdapter, tan extendido de FragmentStatePagerAdapter (por ejemplo en Google ref página: developer.android.com/reference/android/support/v4/view/…). Mi pregunta es: ¿cómo se puede eliminar una ficha en el primer lugar? Gracias.
    • Cuando llamamos a notifyDataSetChanged() el viewpager crea nuevos fragmentos, pero los mayores están todavía en la memoria. Y mi problema es; que recibe una llamada de su onOptionsItemSelected evento. ¿Cómo puedo deshacerme de los fragmentos?
    • No creo que los comentarios son el lugar correcto para preguntas de seguimiento. Si usted tiene preguntas relacionadas a este, por favor enviar una nueva pregunta con referencia a este hilo.
    • Gracias, simplemente sustituyendo FragmentPagerAdapter a un FragmentStatePagerAdapter resuelto mi problema. Después de que acabo de removeAllViews() en ViewPager y de recarga de los fragmentos de nuevo de forma dinámica.
    • Gracias por esto, esta ha sido la conducción me encanta!
    • Gracias por la respuesta, he perdido 2 días para hacerlo, pero tu respuesta me ha ayudado a resolver dentro de 2 minutos…..Muchas gracias
    • Gracias señor !!! yo estaba luchando con este error por dos días.
    • Puede usted por favor me ayude en esto …. m en la gran necesidad que por favor me ayude. stackoverflow.com/questions/21351331/…
    • ¿Por qué siempre te regresan POSITION_NONE en lugar de observar realmente si una determinada vista/fragmento/objeto todavía existe y es válido? Este método se supone que la respuesta a la pregunta «oye, ¿puedo volver a usar esto?» – y siempre diciendo «¡No!», sólo significa que la recreación de todo! Esto no es necesario en muchos casos.
    • Estoy de acuerdo con @Zordid. En mi caso con un montón de imágenes de alta resolución se deslizan a través de ella hacía una pausa por un segundo o así que para recrear los puntos de vista que no es necesario. El cambio a la getItemPosition() de stackoverflow.com/a/13671777/1212314 es una solución mejor.
    • Gracias!! Usted salvó mi día.
    • Eso no fue suficiente para conseguir que las cosas funcionen para mí, ver a mi respuesta
    • Funciona bien, gracias.
    • 🙂 Gracias por esta solución.
    • Increíble respuesta muchas gracias!
    • el uso de @Override public int getItemPosition (Object object) { int index = mFragments.indexOf (object); if (index == -1) return POSITION_NONE; else return index; } tiene más sentido y también me funciona.
    • Recordatorio para mi la pregunta, ¿has visto la pregunta o que estaban ocupados @Louth??
    • Gracias me salvas.
    • Yo ni siquiera tenga que reemplazar getItemPosition() para que esto funcione para mí. FragmentStatePagerAdapter es exactamente lo que estaba buscando.
    • después de unas horas encontrar otra solución, me visitó aquí de nuevo, y cambiar FragmentPagerAdapter a FragmentStatePagerAdapter como antes. Me ayudan a resolver mi problema acerca de cómo quitar la ficha y el fragmento de la tablayout y Viewpager. Esta es la mejor solución.

  3. 13

    mi trabajo solución para eliminar el fragmento de la página de la vista de buscapersonas

    public class MyFragmentAdapter extends FragmentStatePagerAdapter {
    private ArrayList<ItemFragment> pages;
    public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
    super(fragmentManager);
    this.pages = pages;
    }
    @Override
    public Fragment getItem(int index) {
    return pages.get(index);
    }
    @Override
    public int getCount() {
    return pages.size();
    }
    @Override
    public int getItemPosition(Object object) {
    int index = pages.indexOf (object);
    if (index == -1)
    return POSITION_NONE;
    else
    return index;
    }
    }

    Y cuando me tenga que quitar algo de página de índice hago esto

    pages.remove(position); //ArrayList<ItemFragment>
    adapter.notifyDataSetChanged(); //MyFragmentAdapter

    Aquí es mi adaptador de inicialización

    MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
    viewPager.setAdapter(adapter);
    • Se puede por favor compartir el código para añadir el fragmento?
    • Pero, mientras que la adición refleja lento después de deslizar dos/tres veces
    • si no estás eliminando elementos, no sólo devolver el artículo, nada más… están realizando pruebas en la máquina virtual o real del dispositivo?
    • Dispositivo Real….
    • Probar otras soluciones, respuestas con más votos. Ver localizador tiene muy complejo de cobro del sistema para un mejor rendimiento en swip, por lo que este hacks no son manejados por la visión original de localizador con el propósito…
  4. 10

    El fragmento debe ser eliminado ya pero el problema fue viewpager estado de ahorro de

    Intentar

    myViewPager.setSaveFromParentEnabled(false);

    Nada funcionó, pero ya esta resuelto el problema !

    Saludos !

  5. 2

    He tenido la idea de simplemente copiar el código fuente de android.support.v4.app.FragmentPagerAdpater en una clase personalizada con nombre
    CustumFragmentPagerAdapter. Esto me dio la oportunidad de modificar la instantiateItem(...) de modo que cada vez que es llamada, quita destruye la actualidad adjunto fragmento antes de añadir el nuevo fragmento recibido de getItem() método.

    Simplemente modificar el instantiateItem(...) de la siguiente manera:

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
    mCurTransaction = mFragmentManager.beginTransaction();
    }
    final long itemId = getItemId(position);
    //Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    //remove /destroy current fragment
    if (fragment != null) {
    mCurTransaction.remove(fragment);
    }
    //get new fragment and add it
    fragment = getItem(position);
    mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));
    if (fragment != mCurrentPrimaryItem) {
    fragment.setMenuVisibility(false);
    fragment.setUserVisibleHint(false);
    }
    return fragment;
    }
    • Esto también debería funcionar bien.
    • La única respuesta que trabajó
  6. 1

    También se pueden combinar ambos para mejor :

    private class MyPagerAdapter extends FragmentStatePagerAdapter {
    //... your existing code
    @Override
    public int getItemPosition(Object object){
    if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
    return PagerAdapter.POSITION_NONE;
    }
    else
    return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.
    }
  7. 0

    Mi versión final del código, corregido TODOS los errores. Me tomó 3 días

    https://github.com/lin1987www/FragmentBuilder/blob/master/app/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java

    public class FragmentStatePagerAdapterFix extends PagerAdapter {
    private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
    private static final boolean DEBUG = false;
    private WeakReference<FragmentActivity> wrFragmentActivity;
    private WeakReference<Fragment> wrParentFragment;
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;
    protected ArrayList<Fragment> mFragments = new ArrayList<>();
    protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
    protected ArrayList<String> mFragmentTags = new ArrayList<>();
    protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
    protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();
    private Fragment mCurrentPrimaryItem = null;
    private boolean[] mTempPositionChange;
    @Override
    public int getCount() {
    return mFragmentClassNames.size();
    }
    public FragmentActivity getFragmentActivity() {
    return wrFragmentActivity.get();
    }
    public Fragment getParentFragment() {
    return wrParentFragment.get();
    }
    public FragmentStatePagerAdapterFix(FragmentActivity activity) {
    mFragmentManager = activity.getSupportFragmentManager();
    wrFragmentActivity = new WeakReference<>(activity);
    wrParentFragment = new WeakReference<>(null);
    }
    public FragmentStatePagerAdapterFix(Fragment fragment) {
    mFragmentManager = fragment.getChildFragmentManager();
    wrFragmentActivity = new WeakReference<>(fragment.getActivity());
    wrParentFragment = new WeakReference<>(fragment);
    }
    public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
    add(fragClass, null, null);
    }
    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
    add(fragClass, args, null);
    }
    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
    add(fragClass, null, tag);
    }
    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
    add(fragClass, args, tag, getCount());
    }
    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
    mFragments.add(position, null);
    mFragmentStates.add(position, null);
    mFragmentTags.add(position, tag);
    mFragmentClassNames.add(position, fragClass.getName());
    mFragmentArgs.add(position, args);
    mTempPositionChange = new boolean[getCount()];
    }
    public void remove(int position) {
    if (position < getCount()) {
    mTempPositionChange = new boolean[getCount()];
    for (int i = position; i < mTempPositionChange.length; i++) {
    mTempPositionChange[i] = true;
    }
    mFragments.remove(position);
    mFragmentStates.remove(position);
    mFragmentTags.remove(position);
    mFragmentClassNames.remove(position);
    mFragmentArgs.remove(position);
    }
    }
    public void clear(){
    mFragments.clear();
    mFragmentStates.clear();
    mFragmentTags.clear();
    mFragmentClassNames.clear();
    mFragmentArgs.clear();
    }
    @Override
    public void startUpdate(ViewGroup container) {
    }
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment;
    //If we already have this item instantiated, there is nothing
    //to do.  This can happen when we are restoring the entire pager
    //from its saved state, where the fragment manager has already
    //taken care of restoring the fragments we previously had instantiated.
    fragment = mFragments.get(position);
    if (fragment != null) {
    return fragment;
    }
    if (mCurTransaction == null) {
    mCurTransaction = mFragmentManager.beginTransaction();
    }
    FragmentState fs = mFragmentStates.get(position);
    if (fs != null) {
    fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
    //Fix bug
    //http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
    if (fragment.mSavedFragmentState != null) {
    fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
    }
    }
    if (fragment == null) {
    fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
    }
    if (DEBUG) {
    Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
    }
    fragment.setMenuVisibility(false);
    fragment.setUserVisibleHint(false);
    mFragments.set(position, fragment);
    mFragmentStates.set(position, null);
    mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
    return fragment;
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment) object;
    if (mCurTransaction == null) {
    mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG) {
    Log.v(TAG, "Removing item #" + position + ": f=" + object
    + " v=" + ((Fragment) object).getView());
    }
    if (position < getCount()) {
    FragmentState fragmentState = new FragmentState(fragment);
    Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
    if (savedState != null) {
    fragmentState.mSavedFragmentState = savedState.mState;
    }
    mFragmentStates.set(position, fragmentState);
    mFragments.set(position, null);
    }
    mCurTransaction.remove(fragment);
    }
    @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();
    //Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
    for (Fragment fragment : mFragments) {
    if (fragment != null) {
    fixActiveFragment(mFragmentManager, fragment);
    }
    }
    }
    }
    @Override
    public boolean isViewFromObject(View view, Object object) {
    return ((Fragment) object).getView() == view;
    }
    @Override
    public Parcelable saveState() {
    Bundle state = null;
    //目前顯示的 Fragments
    for (int i = 0; i < mFragments.size(); i++) {
    Fragment f = mFragments.get(i);
    if (f != null && f.isAdded()) {
    if (state == null) {
    state = new Bundle();
    }
    String key = "f" + i;
    mFragmentManager.putFragment(state, key, f);
    }
    }
    if (mFragmentStates.size() > 0) {
    if (state == null) {
    state = new Bundle();
    }
    FragmentState[] fs = new FragmentState[mFragmentStates.size()];
    mFragmentStates.toArray(fs);
    state.putParcelableArray("states_fragment", fs);
    }
    return state;
    }
    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
    if (state != null) {
    Bundle bundle = (Bundle) state;
    bundle.setClassLoader(loader);
    Parcelable[] fs = bundle.getParcelableArray("states_fragment");
    mFragments.clear();
    mFragmentStates.clear();
    mFragmentTags.clear();
    mFragmentClassNames.clear();
    mFragmentArgs.clear();
    if (fs != null) {
    for (int i = 0; i < fs.length; i++) {
    FragmentState fragmentState = (FragmentState) fs[i];
    mFragmentStates.add(fragmentState);
    if (fragmentState != null) {
    mFragmentArgs.add(fragmentState.mArguments);
    mFragmentTags.add(fragmentState.mTag);
    mFragmentClassNames.add(fragmentState.mClassName);
    } else {
    mFragmentArgs.add(null);
    mFragmentTags.add(null);
    mFragmentClassNames.add(null);
    }
    mFragments.add(null);
    }
    }
    Iterable<String> keys = bundle.keySet();
    for (String key : keys) {
    if (key.startsWith("f")) {
    int index = Integer.parseInt(key.substring(1));
    Fragment f = mFragmentManager.getFragment(bundle, key);
    if (f != null) {
    f.setMenuVisibility(false);
    mFragments.set(index, f);
    mFragmentArgs.set(index, f.mArguments);
    mFragmentTags.set(index, f.mTag);
    mFragmentClassNames.set(index, f.getClass().getName());
    } else {
    Log.w(TAG, "Bad fragment at key " + key);
    }
    }
    }
    //If restore will change
    notifyDataSetChanged();
    }
    }
    public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
    FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
    if (fm.mActive != null) {
    int index = fragment.mIndex;
    Fragment origin = fm.mActive.get(index);
    if (origin != null) {
    if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
    Log.e(TAG,
    String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
    origin.getClass().getName(), origin.mIndex,
    fragment.getClass().getName(), fragment.mIndex
    ));
    }
    }
    fm.mActive.set(index, fragment);
    }
    }
    //Fix
    //http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
    @Override
    public int getItemPosition(Object object) {
    int index = mFragments.indexOf(object);
    if (index < 0) {
    return PagerAdapter.POSITION_NONE;
    }
    boolean isPositionChange = mTempPositionChange[index];
    int result = PagerAdapter.POSITION_UNCHANGED;
    if (isPositionChange) {
    result = PagerAdapter.POSITION_NONE;
    }
    return result;
    }
    }
  8. 0

    Louth la respuesta que funciona bien. Pero no creo que siempre regreso POSITION_NONE es una buena idea. Porque POSITION_NONE significa que el fragmento debe ser destruido, y un fragmento nuevo será creado.
    Se puede comprobar que en dataSetChanged función en el código fuente de ViewPager.

            if (newPos == PagerAdapter.POSITION_NONE) {
    mItems.remove(i);
    i--;
    ... not related code
    mAdapter.destroyItem(this, ii.position, ii.object);

    Así que yo creo que sería mejor usar un arraylist de weakReference para guardar todos los fragmentos que han creado. Y al agregar o quitar algo de la página, usted puede conseguir la posición correcta de su propio arraylist.

     public int getItemPosition(Object object) {
    for (int i = 0; i < mFragmentsReferences.size(); i ++) {
    WeakReference<Fragment> reference = mFragmentsReferences.get(i);
    if (reference != null && reference.get() != null) {
    Fragment fragment = reference.get();
    if (fragment == object) {
    return i;
    }
    }
    }
    return POSITION_NONE;
    }

    De acuerdo con los comentarios, getItemPosition se Llama cuando la vista de anfitrión está tratando de determinar si la posición de un elemento que ha cambiado. Y el valor de retorno significa su nueva posición.

    Pero esto no es suficiente. Todavía tenemos un paso importante a tomar.
    En el código fuente de FragmentStatePagerAdapter, hay una matriz denominada «mFragments» cachés de los fragmentos que no son destruidos. Y en instantiateItem función.

    if (mFragments.size() > position) {
    Fragment f = mFragments.get(position);
    if (f != null) {
    return f;
    }
    }

    Volvió la caché de fragmentos directamente cuando se encuentra que en caché fragmento no es null. Así que hay un problema. De ejemplo, vamos a eliminar una página en la posición 2, en primer lugar, quitar ese fragmento de nuestra propia referencia arraylist. así, en getItemPosition volverá POSITION_NONE para ese fragmento, y después de que el fragmento será destruido y eliminado de «mFragments».

     mFragments.set(position, null);

    Ahora el fragmento en la posición 3 a la posición 2. Y instantiatedItem con param posición 3 será llamado. En este momento, el tercer elemento en «mFramgents» no es nulo, por lo que repercutirá directamente. Pero en realidad lo que se devuelve es el fragmento en la posición 2. Así que cuando giramos en la página 3, vamos a encontrar una página vacía allí.

    Para evitar este problema. Mi consejo es que usted puede copiar el código fuente de FragmentStatePagerAdapter en su propio proyecto, y cuando se agregan o eliminan las operaciones, debe agregar y quitar elementos de la «mFragments» arraylist.

    Cosas será más simple si usted sólo tiene que utilizar PagerAdapter en lugar de FragmentStatePagerAdapter. La Buena Suerte.

  9. 0

    agregar o quitar fragmento en viewpager dinámicamente.

    Llame setupViewPager(viewPager) en la actividad de inicio.
    Para cargar diferentes fragmento de la llamada setupViewPagerCustom(viewPager).
    por ejemplo, haga clic en el botón de llamada: setupViewPagerCustom(viewPager);

        private void setupViewPager(ViewPager viewPager)
    {
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet1(), "HOME");
    adapter.addFrag(new fragmnet2(), "SERVICES");
    viewPager.setAdapter(adapter);
    }
    private void setupViewPagerCustom(ViewPager viewPager)
    {
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet3(), "Contact us");
    adapter.addFrag(new fragmnet4(), "ABOUT US");
    viewPager.setAdapter(adapter);
    }
    //Viewpageradapter, handles the views
    static class ViewPagerAdapter extends FragmentStatePagerAdapter
    {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();
    public ViewPagerAdapter(FragmentManager manager){
    super(manager);
    }
    @Override
    public Fragment getItem(int position) {
    return mFragmentList.get(position);
    }
    @Override
    public int getCount() {
    return mFragmentList.size();
    }
    @Override
    public int getItemPosition(Object object){
    return PagerAdapter.POSITION_NONE;
    }
    public void addFrag(Fragment fragment, String title){
    mFragmentList.add(fragment);
    mFragmentTitleList.add(title);
    }
    @Override
    public CharSequence getPageTitle(int position){
    return mFragmentTitleList.get(position);
    }
    }
  10. 0

    He tenido algunos problemas con FragmentStatePagerAdapter.
    Después de la eliminación de un elemento:

    • hubo otro elemento que se utiliza para una posición (un elemento que no pertenecen a la posición, pero a una posición próxima a ella)
    • o algún fragmento no fue cargado (sólo había un fondo en blanco visible en la página)

    Después de muchos experimentos, se me ocurrió la siguiente solución.

    public class SomeAdapter extends FragmentStatePagerAdapter {
    private List<Item> items = new ArrayList<Item>();
    private boolean removing;
    @Override
    public Fragment getItem(int position) {
    ItemFragment fragment = new ItemFragment();
    Bundle args = new Bundle();
    //use items.get(position) to configure fragment
    fragment.setArguments(args);
    return fragment;
    }
    @Override
    public int getCount() {
    return items.size();
    }
    @Override
    public int getItemPosition(Object object) {
    if (removing) {
    return PagerAdapter.POSITION_NONE;
    }
    Item item = getItemOfFragment(object);
    int index = items.indexOf(item);
    if (index == -1) {
    return POSITION_NONE;
    } else {
    return index;
    }
    }
    public void addItem(Item item) {
    items.add(item);
    notifyDataSetChanged();
    }
    public void removeItem(int position) {
    items.remove(position);
    removing = true;
    notifyDataSetChanged();
    removing = false;
    }
    }

    Esta solución sólo se usa un hack en el caso de la extracción de un elemento. De lo contrario (por ejemplo, cuando la adición de un elemento) conserva la limpieza y el rendimiento de un código original.

    Por supuesto, desde el exterior del adaptador, que llame sólo addItem/removeItem, no necesita llamar a notifyDataSetChanged().

  11. 0

    De probar esta solución. He usado el enlace de datos para la unión de vista. Usted puede usar «findViewById()» función.

    public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
    private static final String TAG = ActCPExpense.class.getSimpleName();
    private Context mContext;
    private ActCpLossBinding mBinding;
    private ViewPagerAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
    setContentView(R.layout.act_cp_loss);
    mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
    mContext = ActCPExpense.this;
    initViewsAct();
    } catch (Exception e) {
    LogUtils.LOGE(TAG, e);
    }
    }
    private void initViewsAct() {
    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
    mBinding.viewpager.setAdapter(adapter);
    mBinding.tab.setViewPager(mBinding.viewpager);
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem itemActUtility) {
    int i = itemActUtility.getItemId();
    if (i == android.R.id.home) {
    onBackPressed();
    } 
    return super.onOptionsItemSelected(itemActUtility);
    }
    @Override
    public void onClick(View view) {
    super.onClick(view);
    int id = view.getId();
    if (id == R.id.btnAdd) {
    addFragment();
    } else if (id == R.id.btnDelete) {
    removeFragment();
    }
    }
    private void addFragment(){
    adapter.addFragment(FragmentCPPayee.newInstance("Title");
    adapter.notifyDataSetChanged();
    mBinding.tab.setViewPager(mBinding.viewpager);
    }
    private void removeFragment(){
    adapter.removeItem(mBinding.viewpager.getCurrentItem());
    mBinding.tab.setViewPager(mBinding.viewpager);
    }
    class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();
    public ViewPagerAdapter(FragmentManager manager) {
    super(manager);
    }
    @Override
    public int getItemPosition(@NonNull Object object) {
    return PagerAdapter.POSITION_NONE;
    }
    @Override
    public Fragment getItem(int position) {
    return mFragmentList.get(position);
    }
    @Override
    public int getCount() {
    return mFragmentList.size();
    }
    public void addFragment(Fragment fragment, String title) {
    mFragmentList.add(fragment);
    mFragmentTitleList.add(title);
    }
    public void removeItem(int pos) {
    destroyItem(null, pos, mFragmentList.get(pos));
    mFragmentList.remove(pos);
    mFragmentTitleList.remove(pos);
    adapter.notifyDataSetChanged();
    mBinding.viewpager.setCurrentItem(pos - 1, false);
    }
    @Override
    public CharSequence getPageTitle(int position) {
    return "Title " + String.valueOf(position + 1);
    }
    }
    }
  12. 0

    He añadido una función de «clearFragments» y yo que la función para desactivar el adaptador antes de establecer la nueva fragmentos. Esto llama a la adecuada eliminar acciones de los Fragmentos. Mi pagerAdapter clase:

    private class ChartPagerAdapter extends FragmentPagerAdapter{
    private ArrayList<Fragment> fragmentList;
    ChartPagerAdapter(FragmentManager fm){
    super(fm);
    fragmentList = new ArrayList<>();
    }
    void setFragments(ArrayList<? extends Fragment> fragments){
    fragmentList.addAll(fragments);
    }
    void clearFragments(){
    for(Fragment fragment:fragmentList)
    getChildFragmentManager().beginTransaction().remove(fragment).commit();
    fragmentList.clear();
    }
    @Override
    public Fragment getItem(int i) {
    return fragmentList.get(i);
    }
    @Override
    public int getCount() {
    return fragmentList.size();
    }
    }
  13. 0

    Sólo podría invalidar la destroyItem método

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
    }
  14. 0

    he resuelto este problema por estos pasos

    1 – el uso de FragmentPagerAdapter

    2 – en cada fragmento crear un id aleatorio

    fragment.id = new Random().nextInt();

    3 – reemplazar getItemPosition en el adaptador de

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

    4-reemplazar getItemId en el adaptador de

     @Override
    public long getItemId(int position) {
    return mDatasetFragments.get(position).id;
    }

    5 – ahora elimine el código es

     adapter.mDatasetFragments.remove(< item to delete position >);
    adapter.notifyDataSetChanged();

    esto funcionó para mí espero ayudar

  15. -9

    Espero que esto pueda ayudar a lo que usted desea.

    private class MyPagerAdapter extends FragmentStatePagerAdapter {
    //... your existing code
    @Override
    public int getItemPosition(Object object){
    return PagerAdapter.POSITION_UNCHANGED;
    }
    }
    • esto es exactamente lo contrario a la aceptación de contestar – ¿cómo puede funcionar esto??

Dejar respuesta

Please enter your comment!
Please enter your name here