Android agrega espaciado debajo del último elemento en recyclerview con gridlayoutmanager
Estoy intentando agregar el espaciamiento debajo de la fila del último elemento en RecyclerView
con GridLayoutManager
. Utilicé ItemDecoration
encargo para este propósito con el relleno inferior cuando su último elemento como sigue:
public class SpaceItemDecoration extends RecyclerView.ItemDecoration { private int space; private int bottomSpace = 0; public SpaceItemDecoration(int space, int bottomSpace) { this.space = space; this.bottomSpace = bottomSpace; } public SpaceItemDecoration(int space) { this.space = space; this.bottomSpace = 0; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int childCount = parent.getChildCount(); final int itemPosition = parent.getChildAdapterPosition(view); final int itemCount = state.getItemCount(); outRect.left = space; outRect.right = space; outRect.bottom = space; outRect.top = space; if (itemCount > 0 && itemPosition == itemCount - 1) { outRect.bottom = bottomSpace; } } }
Pero el problema con este método es que desordenó las alturas del elemento en la cuadrícula en la última fila. Estoy adivinando que GridLayoutManager
cambia las alturas para los elementos basados en el espaciamiento a la izquierda. ¿Cuál es la manera correcta de lograr esto?
- Establecer margen en RecyclerView mediante programación
- Ordenar los elementos de una visita de reciclaje mediante arrastrar y soltar
- ¿Cómo puedo obtener la posición seleccionada en un RecyclerView?
- Descripción de RecyclerView setHasFixedSize
- El adaptador de RecyclerView notifyDataSetChanged no funciona
Esto funcionará correctamente para un LinearLayoutManager
. Sólo en el caso de un GridLayoutManager
su problemática.
Es muy útil en caso de que tenga un FAB
en la parte inferior y necesite elementos en la última fila para desplazarse por encima de FAB
para que puedan ser visibles.
- RecyclerView notifyItemInserted IllegalStateException
- RecyclerView no recicla las vistas si el recuento de vistas es pequeño
- RecyclerView ItemTouchHelper borrar eliminar animación
- ¿Hay alguna forma de calcular la posición final de desplazamiento
- Ocultar barra de desplazamiento de RecyclerView
- RecyclerView con GridLayoutManager y Picasso mostrando imagen incorrecta
- RecyclerView notifyItemInserted () La animación no se muestra cuando la posición es 0 pero funciona bien con otra posición
- ¿Deberíamos usar RecyclerView para reemplazar ListView?
La solución a este problema consiste en superponer el SpanSizeLookup de GridLayoutManager.
Debe realizar cambios en GridlayoutManager en la Actividad o Fragmento en el que está inflando el RecylerView.
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //your code recyclerView.addItemDecoration(new PhotoGridMarginDecoration(context)); // SPAN_COUNT is the number of columns in the Grid View GridLayoutManager gridLayoutManager = new GridLayoutManager(context, SPAN_COUNT); // With the help of this method you can set span for every type of view gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { if (list.get(position).getType() == TYPE_HEADER) { // Will consume the whole width return gridLayoutManager.getSpanCount(); } else if (list.get(position).getType() == TYPE_CONTENT) { // will consume only one part of the SPAN_COUNT return 1; } else if(list.get(position).getType() == TYPE_FOOTER) { // Will consume the whole width // Will take care of spaces to be left, // if the number of views in a row is not equal to 4 return gridLayoutManager.getSpanCount(); } return gridLayoutManager.getSpanCount(); } }); recyclerView.setLayoutManager(gridLayoutManager); }
Simplemente agregue un relleno y configure android:clipToPadding="false"
<RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="8dp" android:clipToPadding="false" />
¡Gracias a esta maravillosa respuesta !
Lo que puede hacer es agregar un pie de página vacío a su recyclerview. Su relleno será del tamaño de su pie de página.
@Override public Holder onCreateViewHolder( ViewGroup parent, int viewType) { if (viewType == FOOTER) { return new FooterHolder(); } View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); return new Holder(view); } @Override public void onBindViewHolder(final Holder holder, final int position) { //if footer if (position == items.getSize() - 1) { //do nothing return; } //do regular object bindding } @Override public int getItemViewType(int position) { return (position == items.getSize() - 1) ? FOOTER : ITEM_VIEW_TYPE_ITEM; } @Override public int getItemCount() { //add one for the footer return items.size() + 1; }
Puede utilizar el código siguiente para detectar las primeras y últimas filas en una vista de cuadrícula y establecer los desplazamientos superior e inferior correspondientemente.
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { LayoutParams params = (LayoutParams) view.getLayoutParams(); int pos = params.getViewLayoutPosition(); int spanCount = mGridLayoutManager.getSpanCount(); boolean isFirstRow = pos < spanCount; boolean isLastRow = state.getItemCount() - 1 - pos < spanCount; if (isFirstRow) { outRect.top = top offset value here } if (isLastRow) { outRect.bottom = bottom offset value here } } // you also need to keep reference to GridLayoutManager to know the span count private final GridLayoutManager mGridLayoutManager;
Con cosas como esto, se recomienda para resolver usando el ItemDecoration como se significan para eso.
public class ListSpacingDecoration extends RecyclerView.ItemDecoration { private static final int VERTICAL = OrientationHelper.VERTICAL; private int orientation = -1; private int spanCount = -1; private int spacing; public ListSpacingDecoration(Context context, @DimenRes int spacingDimen) { spacing = context.getResources().getDimensionPixelSize(spacingDimen); } public ListSpacingDecoration(int spacingPx) { spacing = spacingPx; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); if (orientation == -1) { orientation = getOrientation(parent); } if (spanCount == -1) { spanCount = getTotalSpan(parent); } int childCount = parent.getLayoutManager().getItemCount(); int childIndex = parent.getChildAdapterPosition(view); int itemSpanSize = getItemSpanSize(parent, childIndex); int spanIndex = getItemSpanIndex(parent, childIndex); /* INVALID SPAN */ if (spanCount < 1) return; setSpacings(outRect, parent, childCount, childIndex, itemSpanSize, spanIndex); } protected void setSpacings(Rect outRect, RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { if (isBottomEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) { outRect.bottom = spacing; } } @SuppressWarnings("all") protected int getTotalSpan(RecyclerView parent) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getSpanCount(); } else if (mgr instanceof StaggeredGridLayoutManager) { return ((StaggeredGridLayoutManager) mgr).getSpanCount(); } else if (mgr instanceof LinearLayoutManager) { return 1; } return -1; } @SuppressWarnings("all") protected int getItemSpanSize(RecyclerView parent, int childIndex) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanSize(childIndex); } else if (mgr instanceof StaggeredGridLayoutManager) { return 1; } else if (mgr instanceof LinearLayoutManager) { return 1; } return -1; } @SuppressWarnings("all") protected int getItemSpanIndex(RecyclerView parent, int childIndex) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanIndex(childIndex, spanCount); } else if (mgr instanceof StaggeredGridLayoutManager) { return childIndex % spanCount; } else if (mgr instanceof LinearLayoutManager) { return 0; } return -1; } @SuppressWarnings("all") protected int getOrientation(RecyclerView parent) { RecyclerView.LayoutManager mgr = parent.getLayoutManager(); if (mgr instanceof LinearLayoutManager) { return ((LinearLayoutManager) mgr).getOrientation(); } else if (mgr instanceof GridLayoutManager) { return ((GridLayoutManager) mgr).getOrientation(); } else if (mgr instanceof StaggeredGridLayoutManager) { return ((StaggeredGridLayoutManager) mgr).getOrientation(); } return VERTICAL; } protected boolean isBottomEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { if (orientation == VERTICAL) { return isLastItemEdgeValid((childIndex >= childCount - spanCount), parent, childCount, childIndex, spanIndex); } else { return (spanIndex + itemSpanSize) == spanCount; } } protected boolean isLastItemEdgeValid(boolean isOneOfLastItems, RecyclerView parent, int childCount, int childIndex, int spanIndex) { int totalSpanRemaining = 0; if (isOneOfLastItems) { for (int i = childIndex; i < childCount; i++) { totalSpanRemaining = totalSpanRemaining + getItemSpanSize(parent, i); } } return isOneOfLastItems && (totalSpanRemaining <= spanCount - spanIndex); } }
He copiado un editado de mi respuesta original aquí, que es en realidad para el espaciado igual, pero es el mismo concepto.
- Firebase Offline Capabilities y addListenerForSingleValueEvent
- Interrupciones de la interfaz de Android 4.2.1, WebView y javascript