Adición de un fondo de color con texto / icono debajo de la fila de swiped cuando se utiliza RecyclerView de Android
EDIT: El problema real era que mi LinearLayout
estaba envuelto en otro diseño, lo que causó el comportamiento incorrecto. La respuesta aceptada por Sanvywell tiene un ejemplo mejor y más completo de cómo dibujar un color bajo vista deslizada que el fragmento de código que proporcioné en la pregunta.
Ahora que el widget RecyclerView tiene soporte nativo para el filtrado de filas con la ayuda de la clase ItemTouchHelper , intento utilizarlo en una aplicación en la que las filas se comportarán de forma similar a la aplicación Bandeja de entrada de Google. Es decir, deslizar hacia el lado izquierdo realiza una acción y deslizar hacia la derecha hace otra.
- Problemas de procesamiento: java.lang.NullPointerException en android.support.v7.widget.RecyclerView en Android Studio 1.1.0
- RecyclerView scrollToPosition no dispara scrollListener
- AppBarLayout no siempre vuelve a entrar en desplazamiento hacia abajo
- ¿Debo cambiar el Listview existente en mi aplicación a RecyclerView?
- RecyclerView dentro de ScrollView no funciona
La implementación de las acciones en sí fue fácil con el método onSwiped
. Sin embargo, no pude encontrar una forma sencilla de establecer el color y el icono que debería aparecer en la vista que se está pasando actualmente (como en la aplicación Bandeja de entrada de Google).
Para hacer eso, estoy tratando de invalidar el método onChildDraw
así:
@Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { RecyclerViewAdapter.ViewHolder vh = (RecyclerViewAdapter.ViewHolder) viewHolder; LinearLayout ll = vh.linearLayout; Paint p = new Paint(); if(dX > 0) { p.setARGB(255, 255, 0, 0); } else { p.setARGB(255, 0, 255, 0); } c.drawRect(ll.getLeft(), ll.getTop(), ll.getRight(), ll.getBottom(), p); super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); }
Determinar la dirección de desplazamiento desde dX y establecer el color adecuado funciona según lo previsto, pero las coordenadas que obtengo de ViewHolder
siempre corresponden al lugar donde se infló el primer LinearLayout
.
¿Cómo consigo las coordenadas correctas para el LinearLayout
que está en la fila actualmente swiped? ¿Hay una manera más fácil (que no requiere para anular onChildDraw
) para establecer el color de fondo y el icono?
- Tengo una Recyclerview y una lista con 5000 ítems pero solo quiero cargar 100 ítems a la vez
- Configuración dinámica de una altura fija para una vista de cuadrícula escalonada
- Youtube Video no se muestra en RecyclerView
- RecyclerView IndexOutOfBoundsException
- Android RecyclerView desplazamiento suave para ver que está animando su altura
- RecyclerView no llama aCreateViewHolder o onBindView
- Eliminar fila de RecyclerView
- ¿Cómo se puede agregar una línea de divisor en un Android RecyclerView?
Yo estaba luchando para implementar esta función también, pero me guió en la dirección correcta.
@Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { // Get RecyclerView item from the ViewHolder View itemView = viewHolder.itemView; Paint p = new Paint(); if (dX > 0) { /* Set your color for positive displacement */ // Draw Rect with varying right side, equal to displacement dX c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom(), p); } else { /* Set your color for negative displacement */ // Draw Rect with varying left side, equal to the item's right side plus negative displacement dX c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom(), p); } super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }
La respuesta aceptada hace un gran trabajo de colorear el fondo, pero no abordar el dibujo del icono.
Esto funcionó para mí, ya que tanto establecer el color de fondo y dibujó el icono, sin que el icono se estiró durante el golpe, o dejar un hueco entre los elementos anteriores y siguientes después de la barrida.
public static final float ALPHA_FULL = 1.0f; public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { // Get RecyclerView item from the ViewHolder View itemView = viewHolder.itemView; Paint p = new Paint(); Bitmap icon; if (dX > 0) { /* Note, ApplicationManager is a helper class I created myself to get a context outside an Activity class - feel free to use your own method */ icon = BitmapFactory.decodeResource( ApplicationManager.getContext().getResources(), R.drawable.myleftdrawable); /* Set your color for positive displacement */ p.setARGB(255, 255, 0, 0); // Draw Rect with varying right side, equal to displacement dX c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom(), p); // Set the image icon for Right swipe c.drawBitmap(icon, (float) itemView.getLeft() + convertDpToPx(16), (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2, p); } else { icon = BitmapFactory.decodeResource( ApplicationManager.getContext().getResources(), R.drawable.myrightdrawable); /* Set your color for negative displacement */ p.setARGB(255, 0, 255, 0); // Draw Rect with varying left side, equal to the item's right side // plus negative displacement dX c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom(), p); //Set the image icon for Left swipe c.drawBitmap(icon, (float) itemView.getRight() - convertDpToPx(16) - icon.getWidth(), (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - icon.getHeight())/2, p); } // Fade out the view as it is swiped out of the parent's bounds final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth(); viewHolder.itemView.setAlpha(alpha); viewHolder.itemView.setTranslationX(dX); } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } private int convertDpToPx(int dp){ return Math.round(dp * (getResources().getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT)); }
No estoy seguro de cómo estas soluciones (por @Sanvywell, @HappyKatz y @ user2410066) están trabajando para ustedes, pero en mi caso necesitaba otro cheque en el método onChildDraw
.
Parece que ItemTouchHelper
mantiene ViewHolder
s de filas eliminadas en caso de que necesiten ser restauradas. También está llamando onChildDraw
para esos VHs además del VH que está siendo swiped. No estoy seguro acerca de las implicaciones de administración de memoria de este comportamiento, pero necesitaba una comprobación adicional en el inicio de onChildDraw
para evitar el dibujo de "fantom" filas.
if (viewHolder.getAdapterPosition() == -1) { return; }
PARTE BONUS:
También he querido continuar dibujando como otras filas animan a sus nuevas posiciones después de que una fila se elimine con el dedo, y no podría hacerlo dentro de ItemTouchHelper
y onChildDraw
. Al final tuve que añadir otro decorador de artículos para hacerlo. Va en esta línea:
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); } }
ACTUALIZACIÓN: He escrito una entrada en el blog de reciclar ver barrido para eliminar la característica. Alguien podría encontrarlo útil. No es necesario un tercero.
Puesto de blog git repo
La solución de HappyKatz tiene un error complicado. ¿Hay alguna razón para dibujar bitmap cuando dX == 0? En algunos casos, esto causa una visibilidad permanente del ícono sobre el elemento de la lista. También los iconos se hacen visibles sobre el artículo de la lista cuando usted apenas toca el artículo de la lista y dX == 1. Para corregir estos:
if (dX > rectOffset) { c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom(), leftPaint); if (dX > iconOffset) { c.drawBitmap(leftBitmap, (float) itemView.getLeft() + padding, (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - leftBitmap.getHeight()) / 2, leftPaint); } } else if (dX < -rectOffset) { c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom(), rightPaint); if (dX < -iconOffset) { c.drawBitmap(rightBitmap, (float) itemView.getRight() - padding - rightBitmap.getWidth(), (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - rightBitmap.getHeight()) / 2, rightPaint); } }
- Lectura de datos de ratón sin procesar en android
- Android Cambiar elementos de menú del cajón de navegación Texto por programación