Eliminar Fragment Page de ViewPager en Android

138

Estoy tratando de agregar y eliminar dinámicamente fragmentos de un ViewPager, agregar trabajos sin ningún problema, pero eliminar no funciona como se esperaba.

Cada vez que quiero eliminar el elemento actual, se elimina el último.

También intenté usar un FragmentStatePagerAdapter o devolver POSITION_NONE en el método getItemPosition del adaptador.

¿Qué estoy haciendo mal?

Aquí hay un ejemplo básico:

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>
Astuetz
fuente
2
+1 para el código fuente
wizurd
¿Es correcto usar un constructor no predeterminado en MyFragment.java??
codeKiller

Respuestas:

95

La solución de Louth no fue suficiente para que las cosas funcionaran para mí, ya que los fragmentos existentes no se destruían. Motivado por esta respuesta , descubrí que la solución es anular el getItemId(int position)método deFragmentPagerAdapter dar una nueva ID única cada vez que haya 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 pestaña o realiza algún cambio en el pedido, debe llamar notifyChangeInPosition(1)antes de llamarnotifyDataSetChanged() , lo que asegurará que se recrearán todos los Fragmentos.

Por qué funciona esta solución

Reemplazar getItemPosition ():

Cuando notifyDataSetChanged()se llama, el adaptador llama al notifyChanged()método al ViewPagerque está conectado. Los ViewPagercheques el valor devuelto por el adaptador de getItemPosition()para cada artículo, eliminando aquellos elementos que de retorno POSITION_NONE(ver el código fuente ) y después de repoblación.

Reemplazar getItemId ():

Esto es necesario para evitar que el adaptador vuelva a cargar el fragmento antiguo cuando ViewPagerse repobla. Puede entender fácilmente por qué esto funciona mirando el código fuente de 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 puede ver, el getItem()método solo se llama si el administrador de fragmentos no encuentra fragmentos existentes con el mismo Id. Para mí, parece un error que los fragmentos antiguos aún estén unidos incluso después de que notifyDataSetChanged()se llama, pero la documentación para ViewPagersí establece claramente que:

Tenga en cuenta que esta clase se encuentra actualmente en fase de diseño y desarrollo inicial. Es probable que la API cambie en las actualizaciones posteriores de la biblioteca de compatibilidad, lo que requerirá cambios en el código fuente de las aplicaciones cuando se compilan con la versión más nueva.

Así que espero que la solución dada aquí no sea necesaria en una versión futura de la biblioteca de soporte.

Tim Rae
fuente
1
Muchas gracias por esta respuesta! Ha sido agotador tratar de resolver este problema sin usar FragmentStatePagerAdapter. Gran explicación también. ¿Has encontrado una manera de hacer que getItem se active al volver al fragmento? He intentado usar su inteligente notifyDataSetChanged () en varios lugares (es decir, onStop) sin éxito
u2tall
Realmente no entiendo tu pregunta ... Publica una nueva pregunta que incluya el código fuente si no puedes resolverlo.
Tim Rae
1
2016, esta solución aún es necesaria para que notifyDataSetChanged()funcione.
Subin Sebastian
¡Guauu! Explicación asombrosa. Tenía un visor bastante complejo donde elimino e inserto elementos dinámicamente y no lo haría funcionar sin comprender todo esto.
rompe el
Todavía necesita esta solución alternativa para reemplazar el fragmento antiguo. Dang it.
kopikaokao
291

ViewPager no elimina sus fragmentos con el código anterior porque carga varias vistas (o fragmentos en su caso) en la memoria. Además de la vista visible, también carga la vista a ambos lados de la vista. Esto proporciona un desplazamiento suave de una vista a otra que hace que ViewPager sea tan genial.

Para lograr el efecto que deseas, debes hacer un par de cosas.

  1. Cambie el FragmentPagerAdapter a un FragmentStatePagerAdapter. La razón de esto es que el FragmentPagerAdapter mantendrá todas las vistas que carga en la memoria para siempre. Donde el FragmentStatePagerAdapter dispone de vistas que quedan fuera de las vistas actuales y transitables.

  2. Anule el método del adaptador getItemPosition (que se muestra a continuación). Cuando llamamos a mAdapter.notifyDataSetChanged();ViewPager, interroga al adaptador para determinar qué ha cambiado en términos de posicionamiento. Utilizamos este método para decir que todo ha cambiado, así que reprocese todo el posicionamiento de su vista.

Y aquí está el código ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}
Boca
fuente
1
Ok, esta parece ser la solución más fácil para este problema. Aunque se discuten otras soluciones en los informes de errores aquí: code.google.com/p/android/issues/detail?id=19110 y aquí: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian el
@Louth: estoy usando ViewPager con TabsAdapter, como se extiende desde FragmentStatePagerAdapter (por ejemplo, en la página de referencia de Google: developer.android.com/reference/android/support/v4/view/… ). Mi pregunta de seguimiento es: ¿cómo eliminas una pestaña en primer lugar? Gracias.
gcl1
44
@Louth Cuando llamamos a notifyDataSetChanged (), el visor crea nuevos fragmentos, pero los más antiguos todavía están en la memoria. Y mi problema es; reciben una llamada para su evento onOptionsItemSelected. ¿Cómo puedo deshacerme de los fragmentos viejos?
syloc
1
Gracias, solo reemplazar FragmentPagerAdapter por un FragmentStatePagerAdapter resolvió mi problema. Después de eso, simplemente elimino AllViews () en ViewPager y vuelvo a cargar fragmentos dinámicamente.
Michal
17
¿Por qué siempre devuelve POSITION_NONE en lugar de mirar realmente si una vista / fragmento / objeto dado todavía existe y es válido? Se supone que este método responde a la pregunta "oye, ¿puedo reutilizar este?" - y siempre diciendo "¡No!" solo significa recreación de todo! Esto es innecesario en muchos casos.
Zordid
14

mi solución de trabajo para eliminar la página de fragmentos del localizador de vistas

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 necesito eliminar alguna página por índice, hago esto

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

Aquí está mi inicialización de adaptador

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
Vasil Valchev
fuente
¿Podrían compartir código para agregar fragmentos?
VVB
Pero al agregarlo, refleja lento después de deslizar dos veces / tres veces
VVB
si no está eliminando elementos, solo devuelve el elemento, nada más ... ¿está probando en vm o dispositivo real?
Vasil Valchev
Pruebe otras soluciones, respuestas con más votos. View pager tiene un sistema de cobro muy complejo para un mejor rendimiento en swip, por lo que estos hacks no son manejados por el visor original con propósito ...
Vasil Valchev
11

El fragmento ya debe haberse eliminado, pero el problema era el estado de guardar del visor

Tratar

myViewPager.setSaveFromParentEnabled(false);

¡Nada funcionó pero esto resolvió el problema!

Salud !

Kathan Shah
fuente
2

Tuve la idea de simplemente copiar el código fuente de android.support.v4.app.FragmentPagerAdpateruna clase personalizada llamada CustumFragmentPagerAdapter. Esto me dio la oportunidad de modificar el instantiateItem(...)para que cada vez que se llame, elimine / destruya el fragmento adjunto actualmente antes de agregar el nuevo fragmento recibido del getItem()método.

Simplemente modifique 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;
}
Sven
fuente
1

Puedes combinar ambos para mejorar:

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.

}
MSaudi
fuente
Esto solo funciona si está eliminando la pestaña del extremo derecho, para eliminar una pestaña interna o la pestaña izquierda, debe reordenar las pestañas y devolver la nueva posición como se indica en la documentación developer.android.com/reference/android/support/v4 / view /…
BitByteDog
1

Para futuros lectores!

Ahora puede usar ViewPager2 para agregar dinámicamente, eliminar fragmentos del visor.

Referencia de API de formulario de cotización

ViewPager2 reemplaza a ViewPager, abordando la mayoría de los puntos débiles de su predecesor, incluido el soporte de diseño de derecha a izquierda, orientación vertical, colecciones de fragmentos modificables, etc.

Tome vistazo a MutableCollectionFragmentActivity.kten googlesample / android-viewpager2 para un ejemplo de la adición, la eliminación de fragmentos dinámicamente desde el ViewPager.


Para tu información:

Artículos:

Referencia API

Notas de lanzamiento

Repo de muestras: https://github.com/googlesamples/android-viewpager2

usuario158
fuente
Tengo 2 fragmentos mainfragment y otherfragment. Estoy agregando otro fragmento después de cada quinta posición y no está en la lista de matrices, entonces, ¿cómo eliminar ese otro fragmento si no está en la lista? Estoy usando viewpager2
b devloper
0

La respuesta de Louth funciona bien. Pero no creo que volver siempre POSITION_NONE sea una buena idea. Porque POSITION_NONE significa que el fragmento debe destruirse y se creará un nuevo fragmento. Puede verificar eso en la función dataSetChanged 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 creo que es mejor usar una lista de arrays de débilReferencia para guardar todos los fragmentos que ha creado. Y cuando agrega o elimina alguna página, puede obtener la posición correcta de su propia lista de arrays.

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

Según los comentarios, getItemPosition se llama cuando la vista de host intenta determinar si la posición de un elemento ha cambiado. Y el valor de retorno significa su nueva posición.

Pero esto no es suficiente. Todavía tenemos un paso importante que dar. En el código fuente de FragmentStatePagerAdapter, hay una matriz llamada "mFragments" que almacena en caché los fragmentos que no se destruyen. Y en la función instantiateItem.

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

Devolvió el fragmento en caché directamente cuando descubrió que el fragmento en caché no es nulo. Entonces hay un problema. Por ejemplo, eliminemos una página en la posición 2, en primer lugar, eliminamos ese fragmento de nuestra propia lista de referencia. entonces en getItemPosition devolverá POSITION_NONE para ese fragmento, y luego ese fragmento será destruido y eliminado de "mFragments".

 mFragments.set(position, null);

Ahora el fragmento en la posición 3 estará en la posición 2. Y se invocará el elemento instanciado con la posición param. 3. En este momento, el tercer elemento en "mFramgents" no es nulo, por lo que regresará directamente. Pero en realidad lo que devolvió es el fragmento en la posición 2. Entonces, cuando pasamos a la página 3, encontraremos una página vacía allí.

Para solucionar este problema. Mi consejo es que puede copiar el código fuente de FragmentStatePagerAdapter en su propio proyecto, y cuando agrega o elimina operaciones, debe agregar y eliminar elementos en la lista de matrices "mFragments".

Las cosas serán más simples si solo usa PagerAdapter en lugar de FragmentStatePagerAdapter. Buena suerte.

transeúnte
fuente
0

Agregar o eliminar fragmentos en el visor de forma dinámica.
Llame a setupViewPager (viewPager) al inicio de la actividad. Para cargar un fragmento diferente, llame a setupViewPagerCustom (viewPager). por ejemplo, al hacer clic en el botón llamar: 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);
    }
}
Riad Hasan
fuente
0

He tenido algunos problemas con FragmentStatePagerAdapter. Después de eliminar un artículo:

  • había otro elemento utilizado para una posición (un elemento que no pertenecía a la posición sino a una posición al lado)
  • o algún fragmento no se cargó (solo había fondo en blanco visible en esa 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 solo usa un truco en caso de eliminar un elemento. De lo contrario (por ejemplo, al agregar un elemento), conserva la limpieza y el rendimiento de un código original.

Por supuesto, desde el exterior del adaptador, solo llama a addItem / removeItem, no es necesario llamar a notifyDataSetChanged ().

Milan Laslop
fuente
0

Prueba esta solución. He usado el enlace de datos para la vista de enlace. Puede usar la función común "findViewById ()".

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


}
Patel Jaimin
fuente
0

Agregué una función "clearFragments" y usé esa función para borrar el adaptador antes de configurar los nuevos fragmentos. Esto llama las acciones de eliminación adecuadas de Fragmentos. Mi clase pagerAdapter:

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

}
Azmi Rutkay BIYIK
fuente
0

resolví este problema siguiendo estos pasos

1- usa FragmentPagerAdapter

2- en cada fragmento crea una identificación aleatoria

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

3- anular getItemPosition en el adaptador

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

4-anular getItemId en el adaptador

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

5- ahora el código de eliminación es

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

esto funcionó para mí espero ayuda

Ali Parsa
fuente
0

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

Actualizado el 20/07/2018: cambié mucho el código fuente y solucioné tantos errores, pero no prometo que todavía funcione hoy.

https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/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;
    }
}
林奕 忠
fuente
Tengo 2 fragmentos mainfragment y otherfragment. Estoy agregando otro fragmento después de cada quinta posición y no está en la lista de matrices, entonces, ¿cómo eliminar ese otro fragmento si no está en la lista? Estoy usando viewpager2
b devloper
el enlace de github no funciona
b devloper
-1

Podrías anular el destroyItemmétodo

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
yausername
fuente
-9

Espero que esto pueda ayudar a lo que quieras.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}
Yin Khaing
fuente
9
Esto es exactamente lo contrario de la respuesta aceptada: ¿cómo puede funcionar?
Zordid