Cómo animar la Adición o Eliminación de filas de Android ListView


En iOS, hay una facilidad 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 se contraen 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 he estado desarrollando en Android no he encontrado ninguna instalación equivalente para animar filas individuales en a tableView. Llamando notifyDataSetChanged() en 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 hacia adentro o deslizándose hacia afuera cuando los datos cambian, pero no puedo encontrar ninguna forma documentada de hacer esto. Parece que LayoutAnimationController podría contener una clave para que esto funcione, pero cuando establezco un LayoutAnimationController en mi ListView (similar a LayoutAnimation2 de ApiDemo) y eliminar elementos de mi adaptador después de que la lista se haya mostrado, los elementos desaparecen inmediatamente en lugar de ser animados.

También he probado 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 hasta que saltan a sus nuevas posiciones cuando se llama notifyDataSetChanged(). Parece que ListView no actualiza su diseño una vez que se han colocado sus elementos.

Mientras escribía mi propio implementación / bifurcación de ListView se me ha pasado por la mente, esto parece algo que no debería ser tan difícil.

Gracias!

Author: Alex Pretzlav, 2010-10-14

14 answers

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 animación de arriba a abajo use:

<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>
 122
Author: OAK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-22 08:58:15
 14
Author: Eric,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-27 20:32:30

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

ListViewRemovalAnimation código del proyecto y Video demostración

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

 9
Author: dimetil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-31 17:36:29

Dado que ListViews están altamente optimizados creo que esto no es posible de accieve. ¿Ha intentado crear su "ListView" por código (es decir, inflando sus filas desde xml y agregándolas a LinearLayout) y animarlas?

 2
Author: whlk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-16 23:31:55

¿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.

 2
Author: Sam Dufel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-17 22:03:26

Llamar ListView.Programación de la animación(); antes de cambiar la lista

 2
Author: karabara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-13 06:07:39

He hackeado otra forma de hacerlo sin tener que manipular la vista de lista. Desafortunadamente, las animaciones regulares de Android parecen manipular el contenido de la fila, pero son ineficaces para reducir la vista. Por lo tanto, 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);
}

Añadir esta colección a su lista adaptador:

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 todo! Puede cambiar la velocidad de la animación por alterar el número de píxeles que reduce la altura a la vez. No es realmente sofisticado, pero funciona!

 2
Author: jkschneider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-16 21:41:56

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

ListView.smoothScrollToPosition(position);
 2
Author: Rafael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-21 11:01:46

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

 1
Author: Genia S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-19 15:38:33

Dado que Android es de código abierto, en realidad no es necesario volver a implementar las optimizaciones de ListView. Puede tomar el código de ListView y tratar de encontrar una manera de hackear la animación, también puede abrir una solicitud de característica en Android bug tracker (y si decidió implementarlo, no se olvide de contribuir con un parche).

Para su información, el código fuente de ListView es aquí.

 1
Author: Lie Ryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-28 21:58:35

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

Un archivo APK DEMO también está disponible. Eliminar filas se hace más a lo largo de las líneas de la aplicación de 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.

 1
Author: AndroidDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-21 10:18:18

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

Por favor, consulte 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 ver esto

Gracias.

 1
Author: Ram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-21 10:28:08

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 onMeasure mientras se emite requestLayout() para la vista de lista. Sí, puede ser mejor hacerlo dentro del código ListView directamente, pero fue una solución rápida (¡eso se veía bien!)

 0
Author: Dori,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-04 13:09:07

Simplemente compartiendo otro enfoque:

Primero establece el android: animateLayoutChanges a true :

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

Luego utilizo 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;
}
 0
Author: Mina Samy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-10 16:01:02