2010-10-13 11 views
204

En iOS, existe una facilidad muy fácil y potente para animar la adición y eliminación de filas de UITableView, here's a clip from a youtube video 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.Cómo animar la adición o la eliminación de las filas de ListView de Android

Como he estado desarrollando en Android, no he encontrado una función equivalente para animar filas individuales en un TableView. Llamar al 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 que funcione, pero cuando configuro LayoutAnimationController en mi ListView (similar a ApiDemo's LayoutAnimation2) y elimino elementos de mi adaptador después de que se muestra la lista, los elementos desaparecen inmediatamente en lugar de animarse.

También he intentado cosas como la siguiente para animar un elemento individual cuando se retira:

@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 de animación no se mueven posición 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 propia implementación/fork de ListView se me pasó por la mente, esto parece algo que no debería ser tan difícil.

Gracias!

+0

alguien f ind la respuesta a esto? pls shareee – AndroidGecko

+0

@Alex: has hecho esa animación como ese video de youtube (como iphone), si tienes una demo o un enlace para Android, por favor dame, he intentado usar animateLayoutChanges en un archivo xml, pero no es exactamente como el iphone – Jayesh

+0

@Jayesh, desafortunadamente ya no trabajo en el desarrollo de Android. No he probado ninguna de las respuestas a esta pregunta que fueron escritas después de 2012. –

Respuesta

122
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 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> 
+69

Es mejor usar 'anim.setAnimationListener (new AnimationListener() {...})' en lugar de usar Handler –

+1

¿para qué es 'populateList()' for? –

+0

@OlegVaskevich y por qué es eso? –

2

Dado que ListViews están muy optimizados, creo que esto no es posible. ¿Has intentado crear tu "ListView" por código (es decir, al inflar tus filas de xml y anexarlas a un LinearLayout) y animarlas?

+6

No tiene sentido volver a implementar la vista de lista solo para agregar animaciones. – Macarse

+0

Ciertamente podría usar un 'LinearLayout', sin embargo, con conjuntos de datos muy grandes, el rendimiento de' LinearLayout' se volverá abismal. 'ListView' tiene muchas optimizaciones inteligentes alrededor del reciclaje de vistas que le permiten manejar grandes conjuntos de datos (el UITableView de iOS tiene las mismas optimizaciones). Escribir mi propio reciclador de vistas cae dentro de la categoría de volver a implementar ListView :( –

+0

Sé que no tiene sentido, pero si trabajas con unas cuantas filas, el beneficio de tener buenas animaciones de entrada/salida podría valer la pena intentarlo. – Mannaz

1

Dado que Android es de código abierto, en realidad no necesita volver a implementar las optimizaciones de ListView. Puede agarrar el código de ListView e intentar encontrar una forma de piratear la animación, también puede open a feature request en el rastreador de errores de Android (y si decidió implementarlo, no se olvide de contribute a patch).

FYI, el código fuente de ListView es here.

+1

Esto no es así la forma en que deberían implementarse tales cambios. Antes de nada, debe tener la construcción del proyecto AOSP, que es un enfoque bastante pesado para agregar animaciones a las vistas de lista. Mire las implementaciones en varias bibliotecas de código abierto. – OrhanC1

2

¿Ha 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 aún se moverían en su lugar, pero sería mejor que nada.

2

Hackeé juntas 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, considerar en primer lugar 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 para 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, dondequiera que se encuentre que desea 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 alterando el número de píxeles que reduce la altura de una vez. No es realmente sofisticado, ¡pero funciona!

1

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?

+0

Hola, gracias Investigaré eso. Desafortunadamente, parece que [animateLayoutChanges] (http://developer.android.com/reference/android/R.attr.html#animateLayoutChanges) es nuevo a partir de API Level 11, también conocido como Honeycomb aka can not -use-this-on-any-phones-yet :( –

0

he hecho algo similar a esto. Un enfoque consiste en interpolar durante el tiempo de animación el alto de la vista a lo largo del tiempo dentro de las filas onMeasure y emitir 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!)

1

Como expliqué mi enfoque en mi sitio, compartí el enlace. De todos modos, la idea es crear mapas de bits por getdrawingcache .have dos mapas de bits y animar 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 see this

Th anks.

2

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

9

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

ListViewRemovalAnimation código de proyecto y Video demostración

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

2

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

ListView.smoothScrollToPosition(position); 
14
+1

Creo que esta es la manera 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. Id estable. ¡Gracias! – auval

+0

Este es un gran proyecto de muestra que demuestra bien la API. –

1

Aquí está la source code para que pueda eliminar filas y reordenarlos.

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.

0

Sólo compartir otro enfoque:

Primero ajuste de la vista de lista android: animateLayoutChanges a cierto:

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

Luego uso un controladorpara añadir 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; 
} 
Cuestiones relacionadas