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>
MyFragment.java
??Respuestas:
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:
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 alnotifyChanged()
método alViewPager
que está conectado. LosViewPager
cheques el valor devuelto por el adaptador degetItemPosition()
para cada artículo, eliminando aquellos elementos que de retornoPOSITION_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
ViewPager
se repobla. Puede entender fácilmente por qué esto funciona mirando el código fuente de instantiateItem () enFragmentPagerAdapter
.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 quenotifyDataSetChanged()
se llama, pero la documentación paraViewPager
sí establece claramente que:Así que espero que la solución dada aquí no sea necesaria en una versión futura de la biblioteca de soporte.
fuente
notifyDataSetChanged()
funcione.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.
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.
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 ...
fuente
mi solución de trabajo para eliminar la página de fragmentos del localizador de vistas
Y cuando necesito eliminar alguna página por índice, hago esto
Aquí está mi inicialización de adaptador
fuente
El fragmento ya debe haberse eliminado, pero el problema era el estado de guardar del visor
Tratar
¡Nada funcionó pero esto resolvió el problema!
Salud !
fuente
Tuve la idea de simplemente copiar el código fuente de
android.support.v4.app.FragmentPagerAdpater
una clase personalizada llamadaCustumFragmentPagerAdapter
. Esto me dio la oportunidad de modificar elinstantiateItem(...)
para que cada vez que se llame, elimine / destruya el fragmento adjunto actualmente antes de agregar el nuevo fragmento recibido delgetItem()
método.Simplemente modifique el
instantiateItem(...)
de la siguiente manera:fuente
Puedes combinar ambos para mejorar:
fuente
Para futuros lectores!
Ahora puede usar ViewPager2 para agregar dinámicamente, eliminar fragmentos del visor.
Referencia de API de formulario de cotización
Tome vistazo a
MutableCollectionFragmentActivity.kt
en 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
fuente
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.
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.
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.
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".
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.
fuente
fuente
He tenido algunos problemas con
FragmentStatePagerAdapter
. Después de eliminar un artículo:Después de muchos experimentos, se me ocurrió la siguiente solución.
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 ().
fuente
Prueba esta solución. He usado el enlace de datos para la vista de enlace. Puede usar la función común "findViewById ()".
fuente
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:
fuente
resolví este problema siguiendo estos pasos
1- usa FragmentPagerAdapter
2- en cada fragmento crea una identificación aleatoria
3- anular getItemPosition en el adaptador
4-anular getItemId en el adaptador
5- ahora el código de eliminación es
esto funcionó para mí espero ayuda
fuente
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
fuente
Podrías anular el
destroyItem
métodofuente
Espero que esto pueda ayudar a lo que quieras.
fuente