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

En iOS, existe una facilidad muy fácil y poderosa para animar la adición y eliminación de filas de 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 viendo cuando cambiaron los datos.

Desde que estoy desarrollando en Android, no he encontrado una función equivalente para animar filas individuales en TableView . Llamar 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 o deslizándose cuando los datos cambian, pero no puedo encontrar ninguna forma documentada de hacerlo. Parece que LayoutAnimationController podría contener una clave para hacer que esto funcione, pero cuando establezco un LayoutAnimationController en mi ListView (similar a LayoutAnimation2 de ApiDemo ) y elimino elementos de mi adaptador después de que se muestra la lista, los elementos desaparecen inmediatamente en lugar de animarse. .

También probé 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 a notifyDataSetChanged() . Parece que ListView no actualiza su diseño una vez que se han colocado sus elementos.

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

¡Gracias!

     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:

         

    ¡ RecyclerView se encarga de agregar, eliminar y reordenar animaciones!

    RecyclerView en acción

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

    1. commit de la clásica aplicación de Hello World para Android
    2. commit, agregando un RecyclerView al proyecto (contenido no dynamic)
    3. confirmar, agregar funcionalidad para modificar el contenido de RecyclerView en tiempo de ejecución (pero no animaciones)
    4. y finalmente … cometer agregar animaciones al RecyclerView

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

    ListViewRemoval Código de proyecto de animación y demostración de video

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

    Como ListViews está altamente optimizado, creo que esto no es posible. ¿Has intentado crear tu “ListView” por código (es decir, inflando tus filas de xml y anexándolas a LinearLayout ) y LinearLayout ?

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

    call listView.scheduleLayoutAnimation (); antes de cambiar la lista

    Arreglé juntos 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. 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 disabledViews = new ArrayList(); 

    y añadir

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

    Luego, donde quiera que quiera 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 de una vez. No es realmente sofisticado, ¡pero funciona!

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

     ListView.smoothScrollToPosition(position); 

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

    Dado que Android es de código abierto, en realidad no necesita volver a implementar las optimizaciones de ListView. Puede tomar el código de ListView y tratar de encontrar una forma 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 se olvide de contribuir con un parche ).

    FYI, el código fuente de ListView está aquí .

    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 hace más a lo largo de las líneas 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.

    Como ya había explicado mi enfoque en mi sitio, compartí el vínculo. De todos modos, la idea es crear mapas de bits mediante getdrawingcache. Tengo dos mapas de bits y animo el bitmap inferior para crear el efecto de movimiento.

    Por favor mira 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.

    He hecho algo similar a esto. Un enfoque consiste en interpolar durante el tiempo de animación el alto de la vista en el tiempo dentro de las filas en onMeasure mientras se emite requestLayout() para listView. Sí, puede ser mejor hacerlo dentro del código listView directamente, pero fue una solución rápida (¡que se veía bien!)

    Solo compartiendo otro enfoque:

    Primero configure el android de la vista de lista : animateLayoutChanges en 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; }