Cómo animar la adición o eliminación de filas de Android ListView

212

En iOS, existe una instalación muy fácil y poderosa para animar la adición y eliminación de filas UITableView, aquí hay un clip de un video de YouTube que muestra la animación predeterminada. Observe cómo las filas circundantes colapsan en la fila eliminada. Esta animación ayuda a los usuarios a realizar un seguimiento de lo que cambió en una lista y en qué parte de la lista estaban mirando cuando cambiaron los datos.

Desde que desarrollé en Android, no encontré ninguna instalación equivalente para animar filas individuales en un TableView . Llamar notifyDataSetChanged()a mi adaptador hace que ListView actualice inmediatamente su contenido con nueva información. Me gustaría mostrar una animación simple de una nueva fila empujando o deslizándose cuando los datos cambian, pero no puedo encontrar ninguna forma documentada de hacerlo. Parece que LayoutAnimationController podría tener una clave para que esto funcione, pero cuando configuro un LayoutAnimationController en mi ListView (similar a LayoutAnimation2 de ApiDemo ) y elimino elementos de mi adaptador después de que la lista se haya mostrado, los elementos desaparecen inmediatamente en lugar de animarse. .

También he intentado cosas como las siguientes para animar un elemento individual cuando se elimina:

@Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
    Animation animation = new ScaleAnimation(1, 1, 1, 0);
    animation.setDuration(100);
    getListView().getChildAt(position).startAnimation(animation);
    l.postDelayed(new Runnable() {
        public void run() {
            mStringList.remove(position);
            mAdapter.notifyDataSetChanged();
        }
    }, 100);
}

Sin embargo, las filas que rodean la fila animada no se mueven de posición hasta que saltan a sus nuevas posiciones cuando notifyDataSetChanged()se llama. Parece que ListView no actualiza su diseño una vez que se han colocado sus elementos.

Si bien escribir mi propia implementación / bifurcación de ListView me ha pasado por la mente, esto parece algo que no debería ser tan difícil.

¡Gracias!

Alex Pretzlav
fuente
¿Alguien encontró la respuesta a esto? pls shareee
AndroidGecko
@Alex: ¿has hecho esa animación como ese video de youtube (como iphone)? Si tienes alguna demostración o enlace para Android, por favor dame, he intentado usar animateLayoutChanges en un archivo xml, pero no es exactamente como iphone
Jayesh
@ Jayesh, desafortunadamente ya no trabajo en el desarrollo de Android. No he probado ninguna de las respuestas a esta pregunta que se escribieron después de alrededor de 2012.
Alex Pretzlav

Respuestas:

124
Animation anim = AnimationUtils.loadAnimation(
                     GoTransitApp.this, android.R.anim.slide_out_right
                 );
anim.setDuration(500);
listView.getChildAt(index).startAnimation(anim );

new Handler().postDelayed(new Runnable() {

    public void run() {

        FavouritesManager.getInstance().remove(
            FavouritesManager.getInstance().getTripManagerAtIndex(index)
        );
        populateList();
        adapter.notifyDataSetChanged();

    }

}, anim.getDuration());

para uso de animación de arriba a abajo:

<set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:fromYDelta="20%p" android:toYDelta="-20"
            android:duration="@android:integer/config_mediumAnimTime"/>
        <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
            android:duration="@android:integer/config_mediumAnimTime" />
</set>
ROBLE
fuente
70
Es mejor usar en anim.setAnimationListener(new AnimationListener() { ... })lugar de usar un
controlador
1
lo que es populateList()para?
Vasily Sochinsky
55
@MrAppleBR por varias razones. Primero, no estás creando una innecesaria Handler. En segundo lugar, hace que el código sea menos complejo al usar una devolución de llamada incorporada para cuando finalice la animación. En tercer lugar, no sabes cómo funcionará la implementación Handlery el Animationfuncionamiento de cada plataforma; Es posible que su ejecución demorada ocurra antes de que finalice la animación.
Oleg Vaskevich
77
"Sin embargo, las filas que rodean la fila animada no se mueven de posición hasta que saltan a sus nuevas posiciones cuando se llama a notifyDataSetChanged ()". Este problema aún persiste en su solución.
Boris Rusev
55
¿Qué es la clase FavouritesManager y el método populateList ()?
Jayesh
14

¡El RecyclerViewse encarga de agregar, eliminar y reordenar animaciones!

RecyclerView en acción

Este sencillo proyecto de AndroidStudio presenta a RecyclerView. Echa un vistazo a los commits :

  1. commit de la clásica aplicación de Android Hello World
  2. commit, agregando un RecyclerView al proyecto (contenido no dinámico)
  3. commit, agregando funcionalidad para modificar el contenido de RecyclerView en tiempo de ejecución (pero sin animaciones)
  4. y finalmente ... cometer añadiendo animaciones a RecyclerView
Eric
fuente
1
Creo que esta es la forma simple cuando necesita una animación simple, pero también puede personalizar la animación utilizando el método RecyclerView.setItemAnimator ().
qatz
2
El último paso es el eslabón perdido que tenía. Identificación estable. ¡Gracias!
Amir Uval
Este es un gran proyecto de muestra que demuestra bien la API.
Mr-IDE
9

Echa un vistazo a la solución de Google. Aquí hay un método de eliminación solamente.

Código de proyecto ListViewRemovalAnimation y demostración de video

Necesita Android 4.1+ (API 16). Pero tenemos 2014 afuera.

dimetil
fuente
2

Como ListViewsestán altamente optimizados, creo que esto no es posible. ¿Ha intentado crear su "ListView" por código (es decir, al inflar sus filas desde xml y agregarlas a a LinearLayout) y animarlas?

whlk
fuente
66
No tiene sentido volver a implementar la vista de lista solo para agregar animaciones.
Macarse
Ciertamente podría usar un LinearLayout, sin embargo, con conjuntos LinearLayoutde datos muy grandes , el rendimiento se volverá abismal. ListViewtiene muchas optimizaciones inteligentes en torno al reciclaje de vistas que le permiten manejar grandes conjuntos de datos (UITableView de iOS tiene las mismas optimizaciones). Al escribir mi propio punto de vista, el reciclador cae en la categoría de reimplementar ListView :(
Alex Pretzlav
Sé que no tiene sentido, pero si trabajas con unas pocas filas, el beneficio de tener buenas animaciones de entrada / salida podría valer la pena.
whlk
2

¿Has considerado animar un barrido a la derecha? Podría hacer algo como dibujar una barra blanca progresivamente más grande en la parte superior del elemento de la lista y luego eliminarla de la lista. Las otras células todavía se moverían en su lugar, pero sería mejor que nada.

Sam Dufel
fuente
2

llame a listView.scheduleLayoutAnimation (); antes de cambiar la lista

Karabara
fuente
2

Pirateé juntos otra forma de hacerlo sin tener que manipular la vista de lista. Desafortunadamente, las animaciones normales de Android parecen manipular el contenido de la fila, pero no son efectivas para reducir la vista. Entonces, primero considere este controlador:

private Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
    Bundle bundle = message.getData();

    View view = listView.getChildAt(bundle.getInt("viewPosition") - 
        listView.getFirstVisiblePosition());

    int heightToSet;
    if(!bundle.containsKey("viewHeight")) {
        Rect rect = new Rect();
        view.getDrawingRect(rect);
        heightToSet = rect.height() - 1;
    } else {
        heightToSet = bundle.getInt("viewHeight");
    }

    setViewHeight(view, heightToSet);

    if(heightToSet == 1)
        return;

    Message nextMessage = obtainMessage();
    bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1);
    nextMessage.setData(bundle);
    sendMessage(nextMessage);
}

Agregue esta colección a su adaptador de lista:

private Collection<Integer> disabledViews = new ArrayList<Integer>();

y añadir

public boolean isEnabled(int position) {
   return !disabledViews.contains(position);
}

A continuación, donde sea que desee ocultar una fila, agregue esto:

Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt("viewPosition", listView.getPositionForView(view));
message.setData(bundle);
handler.sendMessage(message);    
disabledViews.add(listView.getPositionForView(view));

¡Eso es! Puede cambiar la velocidad de la animación alterando el número de píxeles que reduce la altura a la vez. No es realmente sofisticado, ¡pero funciona!

jkschneider
fuente
2

Después de insertar una nueva fila en ListView, simplemente desplazo el ListView a la nueva posición.

ListView.smoothScrollToPosition(position);
Rafael
fuente
1

No lo he probado, pero parece que animarLayoutChanges debería hacer lo que estás buscando. Lo veo en la clase ImageSwitcher, ¿supongo que también está en la clase ViewSwitcher?

Yevgeny Simkin
fuente
Hola, gracias, lo investigaré. Desafortunadamente, parece que animateLayoutChanges es nuevo a partir del nivel de API 11, también conocido como Honeycomb, no se puede usar esto en cualquier teléfono todavía :(
Alex Pretzlav
1

Dado que Android es de código abierto, no es necesario que vuelva a implementar las optimizaciones de ListView. Puede obtener el código de ListView e intentar encontrar una manera de piratear la animación, también puede abrir una solicitud de función en el rastreador de errores de Android (y si decidió implementarlo, no olvide contribuir con un parche ).

Para su información, el código fuente ListView está aquí .

Lie Ryan
fuente
1
Esta no es la forma en que dichos cambios deberían implementarse. Antes que nada, debe tener la construcción del proyecto AOSP, que es un enfoque bastante pesado para agregar animación a las vistas de lista. Mire las implementaciones en varias bibliotecas de código abierto.
OrhanC1
1

Aquí está el código fuente para permitirle eliminar filas y reordenarlas.

Un archivo APK de demostración también está disponible. La eliminación de filas se realiza más en la línea de la aplicación Gmail de Google que revela una vista inferior después de deslizar una vista superior. La vista inferior puede tener un botón Deshacer o lo que quieras.

AndroidDev
fuente
1

Como ya había explicado mi enfoque en mi sitio, compartí el enlace. De todos modos, la idea es crear mapas de bits mediante getdrawingcache .tienen dos mapas de bits y animan el mapa de bits inferior para crear el efecto de movimiento

Por favor vea el siguiente código:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
    {
        public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id)
        {
            listView.setDrawingCacheEnabled(true);
            //listView.buildDrawingCache(true);
            bitmap = listView.getDrawingCache();
            myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom());
            myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight());
            listView.setDrawingCacheEnabled(false);
            imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1));
            imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2));
            imgView1.setVisibility(View.VISIBLE);
            imgView2.setVisibility(View.VISIBLE);
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            lp.setMargins(0, rowView.getBottom(), 0, 0);
            imgView2.setLayoutParams(lp);
            TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight());
            transanim.setDuration(400);
            transanim.setAnimationListener(new Animation.AnimationListener()
            {
                public void onAnimationStart(Animation animation)
                {
                }

                public void onAnimationRepeat(Animation animation)
                {
                }

                public void onAnimationEnd(Animation animation)
                {
                    imgView1.setVisibility(View.GONE);
                    imgView2.setVisibility(View.GONE);
                }
            });
            array.remove(positon);
            adapter.notifyDataSetChanged();
            imgView2.startAnimation(transanim);
        }
    });

Para entender con imágenes vea esto

Gracias.

RAM
fuente
0

He hecho algo similar a esto. Un enfoque es interpolar durante el tiempo de animación la altura de la vista a lo largo del tiempo dentro de las filas onMeasuremientras se emite requestLayout()la lista Vista. Sí, puede ser mejor hacerlo directamente dentro del código listView, pero fue una solución rápida (¡eso se veía bien!)

Dori
fuente
0

Simplemente compartiendo otro enfoque:

Primero configure Android de la vista de lista : animateLayoutChanges en true :

<ListView
        android:id="@+id/items_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"/>

Luego uso un controlador para agregar elementos y actualizar la vista de lista con retraso:

Handler mHandler = new Handler();
    //delay in milliseconds
    private int mInitialDelay = 1000;
    private final int DELAY_OFFSET = 1000;


public void addItem(final Integer item) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mDataSet.add(item);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mAdapter.notifyDataSetChanged();
                        }
                    });
                }
            }).start();

        }
    }, mInitialDelay);
    mInitialDelay += DELAY_OFFSET;
}
Mina Samy
fuente