android - studio - Decorar RecyclerView(con GridLayoutManager) para mostrar el divisor entre los elementos
recyclerview android listview (5)
Aquí hay una implementación más simple y fácil de usar:
public class MediaSpaceDecoration extends RecyclerView.ItemDecoration {
private final int spacing;
private final List<Integer> allowedViewTypes = Arrays.asList(
R.layout.item_image,
R.layout.item_blur);
public MediaSpaceDecoration(int spacing) {
this.spacing = spacing;
}
@Override
public void getItemOffsets(Rect outRect,
View view,
RecyclerView parent,
RecyclerView.State state) {
final int position = parent.getChildAdapterPosition(view);
if (!isMedia(parent, position)) {
return;
}
final int totalSpanCount = getTotalSpanCount(parent);
int spanSize = getItemSpanSize(parent, position);
if (totalSpanCount == spanSize) {
return;
}
outRect.top = isInTheFirstRow(position, totalSpanCount) ? 0 : spacing;
outRect.left = isFirstInRow(position, totalSpanCount) ? 0 : spacing / 2;
outRect.right = isLastInRow(position, totalSpanCount) ? 0 : spacing / 2;
outRect.bottom = 0; // don''t need
}
private boolean isInTheFirstRow(int position, int spanCount) {
return position < spanCount;
}
private boolean isFirstInRow(int position, int spanCount) {
return position % spanCount == 0;
}
private boolean isLastInRow(int position, int spanCount) {
return isFirstInRow(position + 1, spanCount);
}
private int getTotalSpanCount(RecyclerView parent) {
final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
return layoutManager instanceof GridLayoutManager
? ((GridLayoutManager) layoutManager).getSpanCount()
: 1;
}
private int getItemSpanSize(RecyclerView parent, int position) {
final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
return layoutManager instanceof GridLayoutManager
? ((GridLayoutManager) layoutManager).getSpanSizeLookup().getSpanSize(position)
: 1;
}
private boolean isMedia(RecyclerView parent, int viewPosition) {
final RecyclerView.Adapter adapter = parent.getAdapter();
final int viewType = adapter.getItemViewType(viewPosition);
return allowedViewTypes.contains(viewType);
}
}
También outRect
antes de configurar el outRect
porque tengo varios spanSize
s para cada tipo de viewType
y necesito agregar un espacio intermedio adicional solo para los allowedViewTypes
de vista allowedViewTypes
. Puede eliminar fácilmente esa verificación y el código sería aún más simple. Parece esto para mí:
¿Cuál es la manera mejor y más fácil de decorar RecyclerView para tener ese aspecto?
El principal desafío aquí es tener divisores solo entre los elementos, pero no entre los elementos y los bordes izquierdo / derecho de la pantalla.
¿Algunas ideas?
No sé por qué lo necesita, pero esta interfaz de usuario es bastante fácil de implementar con el decorador RecyclerView.
mRecylerView.addItemDecoration(new ItemDecorationAlbumColumns(
getResources().getDimensionPixelSize(R.dimen.photos_list_spacing),
getResources().getInteger(R.integer.photo_list_preview_columns)));
y decorador (necesita un poco de refinamiento)
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class ItemDecorationAlbumColumns extends RecyclerView.ItemDecoration {
private int mSizeGridSpacingPx;
private int mGridSize;
private boolean mNeedLeftSpacing = false;
public ItemDecorationAlbumColumns(int gridSpacingPx, int gridSize) {
mSizeGridSpacingPx = gridSpacingPx;
mGridSize = gridSize;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int frameWidth = (int) ((parent.getWidth() - (float) mSizeGridSpacingPx * (mGridSize - 1)) / mGridSize);
int padding = parent.getWidth() / mGridSize - frameWidth;
int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition();
if (itemPosition < mGridSize) {
outRect.top = 0;
} else {
outRect.top = mSizeGridSpacingPx;
}
if (itemPosition % mGridSize == 0) {
outRect.left = 0;
outRect.right = padding;
mNeedLeftSpacing = true;
} else if ((itemPosition + 1) % mGridSize == 0) {
mNeedLeftSpacing = false;
outRect.right = 0;
outRect.left = padding;
} else if (mNeedLeftSpacing) {
mNeedLeftSpacing = false;
outRect.left = mSizeGridSpacingPx - padding;
if ((itemPosition + 2) % mGridSize == 0) {
outRect.right = mSizeGridSpacingPx - padding;
} else {
outRect.right = mSizeGridSpacingPx / 2;
}
} else if ((itemPosition + 2) % mGridSize == 0) {
mNeedLeftSpacing = false;
outRect.left = mSizeGridSpacingPx / 2;
outRect.right = mSizeGridSpacingPx - padding;
} else {
mNeedLeftSpacing = false;
outRect.left = mSizeGridSpacingPx / 2;
outRect.right = mSizeGridSpacingPx / 2;
}
outRect.bottom = 0;
}
}
Puede obtener su respuesta pero todavía estoy publicando mi solución que puede ayudar a otros. Esto se puede utilizar para listas verticales, horizontales o vistas de cuadrícula al pasar la orientación.
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
public static final int GRID = 2;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST && orientation != GRID) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else if(mOrientation == HORIZONTAL_LIST){
drawHorizontal(c, parent);
} else {
drawVertical(c, parent);
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
if (parent.getChildCount() == 0) return;
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final View child = parent.getChildAt(0);
if (child.getHeight() == 0) return;
final RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin + mDivider.getIntrinsicHeight();
int bottom = top + mDivider.getIntrinsicHeight();
final int parentBottom = parent.getHeight() - parent.getPaddingBottom();
while (bottom < parentBottom) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
top += mDivider.getIntrinsicHeight() + params.topMargin + child.getHeight() + params.bottomMargin + mDivider.getIntrinsicHeight();
bottom = top + mDivider.getIntrinsicHeight();
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + params.rightMargin + mDivider.getIntrinsicHeight();
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else if(mOrientation == HORIZONTAL_LIST) {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
}
}
}
Una solución más simple que funcionó para mí. Espero que pueda ser de utilidad.
class GridItemDecorator(val context: Context, private val spacingDp: Int, private val mGridSize: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val resources = context.resources
val spacingPx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, spacingDp.toFloat(), resources.displayMetrics)
val bit = if (spacingPx > mGridSize) Math.round(spacingPx / mGridSize) else 1
val itemPosition = (view.layoutParams as RecyclerView.LayoutParams).viewAdapterPosition
outRect.top = if (itemPosition < mGridSize) 0 else bit * mGridSize
outRect.bottom = 0
val rowPosition = itemPosition % mGridSize
outRect.left = rowPosition * bit
outRect.right = (mGridSize - rowPosition - 1) * bit
}
}
@BindingAdapter({"bind:adapter"})
public static void bind(RecyclerView view, RecyclerView.Adapter<BaseViewHolder> adapter) {
view.setLayoutManager(new GridLayoutManager(view.getContext(), 3));
view.addItemDecoration(new SpacesItemDecorationGrid(view.getContext(), 4, 3));
view.setItemAnimator(new DefaultItemAnimator());
view.setAdapter(adapter);
}
public class SpacesItemDecorationGrid extends RecyclerView.ItemDecoration {
private int mSizeGridSpacingPx;
private int mGridSize;
private boolean mNeedLeftSpacing = false;
/**
* @param gridSpacingPx
* @param gridSize
*/
SpacesItemDecorationGrid(Context context, int gridSpacingPx, int gridSize) {
mSizeGridSpacingPx = (int) Util.convertDpToPixel(gridSpacingPx, context);
mGridSize = gridSize;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int frameWidth = (int) ((parent.getWidth() - (float) mSizeGridSpacingPx * (mGridSize - 1)) / mGridSize);
int padding = parent.getWidth() / mGridSize - frameWidth;
int itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition();
int itemCount = parent.getAdapter().getItemCount() - mGridSize;
/* if (itemPosition < mGridSize) {
outRect.top = mSizeGridSpacingPx;
} else {
outRect.top = mSizeGridSpacingPx;
}*/
outRect.top = mSizeGridSpacingPx;
if (itemPosition % mGridSize == 0) {
outRect.left = mSizeGridSpacingPx;
outRect.right = padding;
mNeedLeftSpacing = true;
} else if ((itemPosition + 1) % mGridSize == 0) {
mNeedLeftSpacing = false;
outRect.right = mSizeGridSpacingPx;
outRect.left = padding;
} else if (mNeedLeftSpacing) {
mNeedLeftSpacing = false;
outRect.left = mSizeGridSpacingPx - padding;
if ((itemPosition + 2) % mGridSize == 0) {
outRect.right = mSizeGridSpacingPx - padding;
} else {
outRect.right = mSizeGridSpacingPx / 2;
}
} else if ((itemPosition + 2) % mGridSize == 0) {
mNeedLeftSpacing = false;
outRect.left = mSizeGridSpacingPx / 2;
outRect.right = mSizeGridSpacingPx - padding;
} else {
mNeedLeftSpacing = false;
outRect.left = mSizeGridSpacingPx / 2;
outRect.right = mSizeGridSpacingPx / 2;
}
if (itemPosition > itemCount) {
outRect.bottom = mSizeGridSpacingPx;
} else {
outRect.bottom = 0;
}
}
}