staggeredgridlayoutmanager - recyclerview android studio ejemplo
Android agrega espacio debajo del Ășltimo elemento en la vista de reciclaje con gridlayoutmanager (5)
Estoy tratando de agregar espacio debajo de la última fila de elementos en
RecyclerView
con
GridLayoutManager
.
ItemDecoration
personalizado para este propósito con relleno inferior cuando su último elemento es el siguiente:
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 estropeó las alturas de los elementos en la cuadrícula en la última fila.
Supongo que
GridLayoutManager
cambia las alturas de los elementos en función del espacio a la izquierda.
¿Cuál es la forma correcta de lograr esto?
Esto funcionará correctamente para un
LinearLayoutManager
.
Solo en el caso de un
GridLayoutManager
es problemático.
Es muy útil en caso de que tenga una
FAB
en la parte inferior y necesite elementos en la última fila para desplazarse por encima de la
FAB
para que puedan ser visibles.
Con cosas como esta, se recomienda resolver usando el ItemDecoration, ya que están destinadas 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);
}
}
Copié un editado de mi respuesta original here que en realidad es para un espacio igual pero es el mismo concepto.
La solución a este problema radica en anular el SpanSizeLookup de GridLayoutManager.
Debe realizar cambios en el GridlayoutManager en la Actividad o Fragmento donde está inflando 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);
}
Lo que puede hacer es agregar un pie de página vacío a su vista de reciclador. 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 usar el siguiente código para detectar la primera y la última fila en una vista de cuadrícula y establecer las compensaciones superior e inferior de manera correspondiente.
@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;
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 answer !