studio recyclerview make how dependency create android android-recyclerview

android - make - recyclerview in fragment



RecyclerView ItemDecoration Spacing and Span (2)

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 lo suficientemente claro para leerlo como desarrollador de Java;) Por lo tanto, la suposición principal es siempre agregar sapce al lado superior e izquierdo del elemento. Ahora, solo necesitamos añadir espacio en el lado derecho, para ello necesitamos saber qué elemento está a la derecha de la fila.

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

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

Lo sé, esa no es la mejor solución, porque cuando tienes muchos elementos en RecycelrView puede tardar un tiempo en calcular. Para evitarlo, puede contar algunos arrayList, que contiene la posición de la vista y allSpanSize para el elemento dado.

Tengo una clase GridSpacingItemDecoration que administra el espaciado y los tramos.
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 a continuación - todos los espaciamientos y trabajos de tramo)


El problema es que quiero usar un RecyclerView que tiene diferentes ViewTypes con diferentes spanCount. así es como intenté hacerlo:
definido en 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:

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


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

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

Esos 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 forma puede verificar en qué columna se encuentra una vista y cuántas columnas abarca .

  • Si está en la columna 0 , aplica el desplazamiento completo en el lado de inicio, sino solo la mitad

  • Si spanIndex + spanSize es igual a spanCount (ocupa la última columna) aplica la compensación completa al final, sino sólo la mitad.

Para una mejor reutilización también debes considerar usar

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

para obtener el recuento de los tramos totales en lugar de establecerlo en el constructor. De esta forma, puede cambiar / actualizar dinámicamente el recuento de tramos y seguirá funcionando.

Por favor, no te olvides de verificar instanceof antes de lanzar y lanzar excepciones apropiadas o algo así;)

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 las alineará correctamente.