swipetodeletecallback recyclerview item delete android swipe android-recyclerview

android - delete - RecyclerView ItemTouchHelper deslizar eliminar animación



swipe to dismiss android (3)

Tengo una eliminación al deslizar, que dibuja un fondo (como la aplicación de la Bandeja de entrada), implementado por ItemTouchHelper - al reemplazar el método onChilDraw y dibujar un rectángulo en el lienzo provisto:

ItemTouchHelper mIth = new ItemTouchHelper( new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) { public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { remove(viewHolder.getAdapterPosition()); } public boolean onMove(RecyclerView recyclerview, RecyclerView.ViewHolder v, RecyclerView.ViewHolder target) { return false; } @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { View itemView = viewHolder.itemView; Drawable d = ContextCompat.getDrawable(context, R.drawable.bg_swipe_item_right); d.setBounds(itemView.getLeft(), itemView.getTop(), (int) dX, itemView.getBottom()); d.draw(c); super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } });

El método de eliminación llamado arriba está en el Adaptador:

public void remove(int position) { items.remove(position); notifyItemRemoved(position); }

El fondo se dibuja muy bien, pero cuando se llama a NotifyItemRemoved (según el Sr. Debugger), RecyclerView primero elimina mi fondo bastante verde y luego junta los dos elementos adyacentes.

Me gustaría mantener el fondo allí mientras lo hace (al igual que la aplicación Bandeja de entrada). ¿Hay alguna forma de hacer eso?


Solo actualiza la posición del adaptador y luego elimina la animación

@Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { int position = viewHolder.getAdapterPosition(); if (direction == ItemTouchHelper.LEFT) { remove(position); } else { mAdapter.notifyItemChanged(position); } }


Tuve el mismo problema y no quería introducir una nueva biblioteca solo para solucionarlo. El RecyclerView no está eliminando su fondo bastante verde, solo se está redibujando, y su ItemTouchHelper ya no está dibujando. En realidad se está dibujando, pero el dX es 0 y se está dibujando desde itemView.getLeft() (que es 0) a dX (que es 0) para que no vea nada. Y está dibujando demasiado, pero volveré más tarde.

De todos modos, de vuelta al fondo mientras las filas se animan: no pude hacerlo dentro de ItemTouchHelper y onChildDraw . Al final tuve que agregar otro elemento decorador para hacerlo. Va a lo largo de estas líneas:

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if (parent.getItemAnimator().isRunning()) { // find first child with translationY > 0 // draw from it''s top to translationY whatever you want int top = 0; int bottom = 0; int childCount = parent.getLayoutManager().getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getLayoutManager().getChildAt(i); if (child.getTranslationY() != 0) { top = child.getTop(); bottom = top + (int) child.getTranslationY(); break; } } // draw whatever you want super.onDraw(c, parent, state); } }

Este código solo tiene en cuenta las filas que se animan, pero también debe considerar que las filas estén bajando. Eso sucede si se desliza para eliminar la última fila, las filas de arriba se animarán hasta ese espacio.

Cuando dije que su ItemTouchHelper está dibujando demasiado, lo que quise decir era: Parece que ItemTouchHelper mantiene ViewHolder en las filas eliminadas en caso de que necesiten ser restauradas. También se está solicitando onChildDraw para esos VH además del VH que se está deslizando. No estoy seguro de las implicaciones de la gestión de la memoria de este comportamiento, pero necesitaba un control adicional al inicio de onChildDraw para evitar dibujar filas "fantásticas".

if (viewHolder.getAdapterPosition() == -1) { return; }

En su caso, se dibuja de izquierda = 0 a derecha = 0, por lo que no ve nada, pero la sobrecarga está ahí. Si empiezas a ver filas previamente eliminadas dibujando sus fondos, esa es la razón.

EDITAR: tuve una oportunidad en esto, ver esta entrada de blog y este repositorio github .


Logré que funcionara utilizando la biblioteca recyclerview-animators de Wasabeefs .

My ViewHolder ahora amplía el AnimateViewHolder proporcionado por la biblioteca:

class MyViewHolder extends AnimateViewHolder { TextView textView; public MyViewHolder(View itemView) { super(itemView); this.textView = (TextView) itemView.findViewById(R.id.item_name); } @Override public void animateAddImpl(ViewPropertyAnimatorListener listener) { ViewCompat.animate(itemView) .translationY(0) .alpha(1) .setDuration(300) .setListener(listener) .start(); } @Override public void preAnimateAddImpl() { ViewCompat.setTranslationY(itemView, -itemView.getHeight() * 0.3f); ViewCompat.setAlpha(itemView, 0); } @Override public void animateRemoveImpl(ViewPropertyAnimatorListener listener) { ViewCompat.animate(itemView) .translationY(0) .alpha(1) .setDuration(300) .setListener(listener) .start(); } }

Las implementaciones de funciones anuladas son idénticas a las que se encuentran en el archivo readme de animators de recyclerview en github.

También parece necesario cambiar el ItemAnimator a uno personalizado y establecer removeDuration en 0 (u otro valor bajo, esto es para evitar algunos parpadeos):

recyclerView.setItemAnimator(new SlideInLeftAnimator()); recyclerView.getItemAnimator().setRemoveDuration(0);

Esto no causa ningún problema, ya que la animación de eliminación normal (sin deslizamiento) utilizada es la de AnimateViewHolder.

Todos los demás códigos se mantuvieron igual que en la pregunta. Todavía no he tenido tiempo de averiguar el funcionamiento interno de esto, pero si alguien tiene ganas de hacerlo, siéntase libre de actualizar esta respuesta.

Actualización: configurando recyclerView.getItemAnimator().setRemoveDuration(0); En realidad rompe la animación "rebindir" del golpe. Afortunadamente, eliminar esa línea y establecer una duración más larga en animateRemoveImpl (500 funciona para mí) también resuelve el problema de parpadeo.

Actualización 2: Resulta que ItemTouchHelper.SimpleCallback usa las duraciones de animación de ItemAnimator, por lo que el conjunto anterior Eliminar la duración (0) rompe la animación de barrido. Simplemente anulando su método getAnimationDuration para:

@Override public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) { return animationType == ItemTouchHelper.ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION : DEFAULT_SWIPE_ANIMATION_DURATION; }

resuelve ese problema