android - LayoutManager para RecyclerView Grid con diferente ancho de celda
gridlayoutmanager (2)
StaggeredGridLayoutManager
no parece permitir personalizar el ancho de una celda o abarcar varias columnas (excepto el tramo completo) para la orientación vertical.
¿Qué es un LayoutManager
preferido para organizar celdas como se muestra arriba?
PS Solo quiero saber cómo personalizar el ancho de celda y no la altura con StaggeredGridLayoutManager
. Sé que la altura se puede personalizar como se implementa en esta sample .
public class VerticalStaggeredGridFragment extends RecyclerFragment {
public static VerticalStaggeredGridFragment newInstance() {
VerticalStaggeredGridFragment fragment = new VerticalStaggeredGridFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
protected RecyclerView.LayoutManager getLayoutManager() {
return new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
}
@Override
protected RecyclerView.ItemDecoration getItemDecoration() {
return new InsetDecoration(getActivity());
}
@Override
protected int getDefaultItemCount() {
return 100;
}
@Override
protected SimpleAdapter getAdapter() {
return new SimpleStaggeredAdapter();
}
}
Adaptador
public class SimpleStaggeredAdapter extends SimpleAdapter {
@Override
public void onBindViewHolder(VerticalItemHolder itemHolder, int position) {
super.onBindViewHolder(itemHolder, position);
final View itemView = itemHolder.itemView;
if (position % 4 == 0) {
int height = itemView.getContext().getResources()
.getDimensionPixelSize(R.dimen.card_staggered_height);
itemView.setMinimumHeight(height);
} else {
itemView.setMinimumHeight(0);
}
}
}
itemView.setMinimumWidth(customWidth)
no afecta el ancho de la celda.
Para simplificar, actualizo mi diseño de cuadrícula a
¿Cómo aumentar el ancho de la celda 0 en comparación con la celda 1 en un StaggeredGridLayoutManager
o cualquier otro administrador de diseño?
Para todos los demás, si busca una solución sin simplificar su diseño, puede consultar mi respuesta aquí o leer más.
Muchas gracias a Nick Butcher ! Su administrador de diseño llamado SpannableGridLayoutManager
es capaz de hacer esto.
Descargue SpannableGridLayoutManager desde aquí
Por alguna razón tuve que cambiar esta línea:
while (availableSpace > 0 && lastVisiblePosition < lastItemPosition) {
a
while (lastVisiblePosition < lastItemPosition) {
para que el gerente trabaje.
Establezca SpannableGridLayoutManger en su RecyclerView
En tu caso:
SpannedGridLayoutManager manager = new SpannedGridLayoutManager(
new SpannedGridLayoutManager.GridSpanLookup() {
@Override
public SpannedGridLayoutManager.SpanInfo getSpanInfo(int position) {
switch (position % 8) {
case 0:
case 5:
return new SpannedGridLayoutManager.SpanInfo(2, 2);
case 3:
case 7:
return new SpannedGridLayoutManager.SpanInfo(3, 2);
default:
return new SpannedGridLayoutManager.SpanInfo(1, 1);
}
}
},
3, // number of columns
1f // default size of item
);
Lo que le dará exactamente lo que está en la primera imagen de la pregunta.
Puede usar StaggeredGridLayoutManager.LayoutParams
para cambiar el ancho y el alto de las celdas. Lo que espera diseñar sigue un patrón simple. Si asigna un índice a cada celda (en el orden en que StaggeredGridLayoutManager
ordena por defecto) tendrá esto:
index % 4 == 3 ---> full span cell
index % 8 == 0, 5 ---> half span cell
index % 8 == 1, 2, 4, 6 ---> quarter span cell
Primero declare algunas constantes en el adaptador para definir tipos de tramas:
private static final int TYPE_FULL = 0;
private static final int TYPE_HALF = 1;
private static final int TYPE_QUARTER = 2;
Luego getItemViewType
método getItemViewType
en su adaptador de esta manera:
@Override
public int getItemViewType(int position) {
final int modeEight = position % 8;
switch (modeEight) {
case 0:
case 5:
return TYPE_HALF;
case 1:
case 2:
case 4:
case 6:
return TYPE_QUARTER;
}
return TYPE_FULL;
}
Todo lo que necesita hacer es cambiar layoutparams considerando viewType
del holder
:
@Override
public MyHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View itemView =
LayoutInflater.from(mContext).inflate(R.layout.items, parent, false);
itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final int type = viewType;
final ViewGroup.LayoutParams lp = itemView.getLayoutParams();
if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {
StaggeredGridLayoutManager.LayoutParams sglp =
(StaggeredGridLayoutManager.LayoutParams) lp;
switch (type) {
case TYPE_FULL:
sglp.setFullSpan(true);
break;
case TYPE_HALF:
sglp.setFullSpan(false);
sglp.width = itemView.getWidth() / 2;
break;
case TYPE_QUARTER:
sglp.setFullSpan(false);
sglp.width = itemView.getWidth() / 2;
sglp.height = itemView.getHeight() / 2;
break;
}
itemView.setLayoutParams(sglp);
final StaggeredGridLayoutManager lm =
(StaggeredGridLayoutManager) ((RecyclerView) parent).getLayoutManager();
lm.invalidateSpanAssignments();
}
itemView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
});
MyHolder holder = new MyHolder(itemView);
return holder;
}
Mi adaptador siempre devuelve 50 para el recuento de elementos (solo una prueba) y utilicé un archivo de diseño simple, contiene un LinearLayout
y un TextView
para mostrar la posición del titular. Recuerde que debe pasar 2 para spanCount
(en el constructor StaggeredGridLayoutManager
) debido a su diseño.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
StaggeredGridLayoutManager lm =
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
rv.setLayoutManager(lm);
rv.setAdapter(new MyAdapter(this));
}
PD : para las personas perezosas como yo, tal vez sea una forma más sencilla en lugar de extender mi LayoutManager
personalizado, pero estoy seguro de que hay mejores formas de lograrlo.
Actualización : la parte actualizada de su pregunta es más simple, puede usar GridLayoutManager
:
GridLayoutManager glm = new GridLayoutManager(this, 3);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position % 3 == 2) {
return 3;
}
switch (position % 4) {
case 1:
case 3:
return 1;
case 0:
case 2:
return 2;
default:
//never gonna happen
return -1 ;
}
}
});
rv.setLayoutManager(glm);
De esta manera, configura 3 para el tamaño de intervalo predeterminado, luego, considerando la posición de su intervalo, cada elemento ocupa 1, 2 o 3 intervalos, algo como esto:
Item0.width == 2 X Item1.width
Item2.width == 3 X Item1.width
Item0.width == 2/3 X Item1.width