RecyclerView ItemDecoration Espaciado y Span

Tengo una clase de GridSpacingItemDecoration que maneja el espaciamiento y las extensiones.
Aquí está el código:

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private int spanCount; private int spacing; private boolean includeEdge; private boolean rtl; public GridSpacingItemDecoration(boolean rtl, int spanCount, int spacing, boolean includeEdge) { this.rtl = rtl; this.spanCount = spanCount; this.spacing = spacing; this.includeEdge = includeEdge; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view); // item position int column = position % spanCount; // item column if (includeEdge) { if (rtl) { outRect.right = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.left = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) }else { outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) } if (position < spanCount) { // top edge outRect.top = spacing; } outRect.bottom = spacing; // item bottom } else { if (rtl){ outRect.right = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.left = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) }else { outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) } if (position >= spanCount) { outRect.top = spacing; // item top } } } } 

Funciona bien cuando quiero tener una o más columnas. (Se muestra en las imágenes siguientes – todas las obras de espaciado y de tramo)
Introduzca aquí la descripción de la imagen
Introduzca aquí la descripción de la imagen
El problema es que quiero usar un RecyclerView que tiene diferentes ViewTypes con spanCount diferente. Aquí es cómo intenté hacerlo:
Definidos en la clase:

 public static ArrayList<Integer> type = new ArrayList<>(); private int getTypeForPosition(int position) { return type.get(position); } private final int HEADER = 0; private final int CHILD = 1; private int dpToPx(int dp) { Resources r = getResources(); return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics())); } 

Definido en el método:

 type.add(HEADER); type.add(CHILD); type.add(CHILD); type.add(HEADER); GridLayoutManager glm = new GridLayoutManager(getContext(), 2); glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { switch(getTypeForPosition(position)) { case HEADER: return 2; default: return 1; } } }); recyclerView.setLayoutManager(glm); recyclerView.addItemDecoration(new GridSpacingItemDecoration(true, 1, dpToPx(8), true)); ClassAdapter classAdapter = new ClassAdapter(getContext(), classes); recyclerView.setAdapter(classAdapter); 

Aquí está el resultado:
Introduzca aquí la descripción de la imagen

El problema es: el espacio entre dos columnas en la misma fila (se muestra en la imagen). Parece ser 16, dos veces lo que he elegido.
Pregunta: ¿Cómo personalizar la clase GridSpacingItemDecoration para tener el mismo espacio entre todos los elementos?

Aquí está mi proposición:

 class SearchResultItemDecoration(val space: Int, val NUMBER_OF_COLUMNS: Int) : RecyclerView.ItemDecoration() { override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) { super.getItemOffsets(outRect, view, parent, state) addSpaceToView(outRect, parent?.getChildAdapterPosition(view), parent) } private fun addSpaceToView(outRect: Rect?, position: Int?, parent: RecyclerView?) { if (position == null || parent == null) return val grid = parent.layoutManager as GridLayoutManager val spanSize = grid.spanSizeLookup.getSpanSize(position) if (spanSize == NUMBER_OF_COLUMNS) { outRect?.right = space } else { var allSpanSize = 0 for (i: Int in IntRange(0, position)) { allSpanSize += grid.spanSizeLookup.getSpanSize(i) } val currentModuloResult = allSpanSize % NUMBER_OF_COLUMNS if (currentModuloResult == 0) { outRect?.right = space } } outRect?.left = space outRect?.top = space } } 

El código está escrito en Kotlin, pero espero que sea suficiente para leerlo como un desarrollador de Java;) Por lo tanto, la suposición principal es añadir siempre sapce a la parte superior e izquierda del elemento. Ahora, solo necesitamos wory para agregar espacio en el lado derecho, para hacer esto necesitamos saber qué elemento está a la derecha de la fila.

spanSize es el valor que contiene información sobre cuántas columnas está tomando la vista actual. Si toma todas las columnas de la fila, obviamente, también wanto para agregar espacio a la derecha.

Esa fue una situación sencilla. Ahora si queremos agregar espacio a un elemento, que también está en el lado derecho de RecycelrView necesitamos calcularlo. allSpanSize es un valor que no cuenta la posición del artículo, sino el tamaño del intervalo del artículo. De esa manera todo lo que necesitamos hacer ahora es hacer matemáticas simples con operación de módulo. Si el resto de la división es 0, ahora el elemento actual está a la derecha.

Lo sé, que quizás no es la mejor solución, porque cuando tienes un montón de artículos en RecycelrView puede tomar algún tiempo para calcular. Para evitarlo, podría contar alguna arrayList, que mantiene la posición de la vista y allSpanSize para el elemento dado.

La forma de hacerlo es leer los parámetros de diseño de la vista.

 GridLayoutManager.LayoutParams params = (GridLayoutManager.LayoutParams) view.getLayoutParameters() 

Los parámetros de diseño tienen las siguientes propiedades:

 // Returns the current span index of this View. int getSpanIndex() // Returns the number of spans occupied by this View. int getSpanSize() 

De esta manera puede comprobar en qué columna se encuentra una vista y cuántas columnas ocupa.

  • Si está en la columna 0 se aplica el desplazamiento completo en el lado de inicio, de lo contrario sólo la mitad

  • Si spanIndex + spanSize es igual a spanCount (ocupa la última columna) se aplica el desplazamiento completo en el extremo, de lo contrario sólo la mitad.

Para una mejor reutilización, también debe considerar el uso de

 ((GridLayoutManager) parent.getLayoutManager()).getSpanCount() 

Para obtener el recuento de spans totales en lugar de configurarlo en el constructor. De esta manera puede cambiar dinámicamente / actualizar el recuento de span y todavía funcionará.

Por favor, no se olvide de comprobar instanceof antes de lanzar y lanzar las excepciones adecuadas o algo;)


Siguiendo estas instrucciones a la carta, terminamos con la siguiente decoración:

 class GridSpanDecoration extends RecyclerView.ItemDecoration { private final int padding; public GridSpanDecoration(int padding) { this.padding = padding; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); GridLayoutManager gridLayoutManager = (GridLayoutManager) parent.getLayoutManager(); int spanCount = gridLayoutManager.getSpanCount(); GridLayoutManager.LayoutParams params = (GridLayoutManager.LayoutParams) view.getLayoutParams(); int spanIndex = params.getSpanIndex(); int spanSize = params.getSpanSize(); // If it is in column 0 you apply the full offset on the start side, else only half if (spanIndex == 0) { outRect.left = padding; } else { outRect.left = padding / 2; } // If spanIndex + spanSize equals spanCount (it occupies the last column) you apply the full offset on the end, else only half. if (spanIndex + spanSize == spanCount) { outRect.right = padding; } else { outRect.right = padding / 2; } // just add some vertical padding as well outRect.top = padding / 2; outRect.bottom = padding / 2; if(isLayoutRTL(parent)) { int tmp = outRect.left; outRect.left = outRect.right; outRect.right = tmp; } } @SuppressLint({"NewApi", "WrongConstant"}) private static boolean isLayoutRTL(RecyclerView parent) { return parent.getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL; } } 

Que permite una cantidad arbitraria de columnas y alinearlos correctamente.

Decoración Grid Span

  • Crear menú de opciones para RecyclerView-Item
  • Google Play Store como interfaz utilizando la vista de reciclador
  • ¿Cómo puedo crear un RecyclerView circular (sin fin)?
  • Mostrar la vista personalizada bajo el elemento RecyclerView deslizado
  • ¿Cómo puede RecyclerView ItemTouchHelper bloqueo deslizar artículo?
  • Transición de elemento compartido en RecyclerView
  • Desplazamiento Horizontal Liso de Recyclerview dentro de Scrollview
  • Recycleview muestra diferentes tipos de vistas
  • Cómo abrir una actividad diferente en recicladorVer artículo onclick
  • RecyclerVista la lista con los artículos con los límites en un ángulo, no horizontal
  • Confirmación y eliminación de deshacer en RecyclerView
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.