Diseño cuadrado en GridLayoutManager para RecyclerView
Trato de hacer un grid-layout con imágenes cuadradas. Pensé que debe ser posible manipular el GridLayoutManager
manipulando onMeasure
para hacer un
super.onMeasure(recycler, state, widthSpec, widthSpec);
en lugar de
- Elementos compartidos que animan entre fragmentos
- Los elementos no tienen el mismo ancho cuando se utiliza RecyclerView GridLayoutManager para establecer el espaciado de columnas por ItemDecoration
- Descripción de RecyclerView.ViewHolder
- Configurar RecyclerView para trabajar como chat
- Horizontalmente en RecyclerView (con GridLayoutManager)
super.onMeasure(recycler, state, widthSpec, heightSpec);
Pero desafortunadamente, eso no funcionó.
¿Algunas ideas?
- La altura de la vista Recycler anidada no envuelve su contenido
- ¿Cómo utilizar ItemAnimator en un RecyclerView?
- Prueba recyclerView con Espresso, cómo realizar clics o hacer afirmaciones
- No se puede escribir un algoritmo para filtrar elementos en un RecyclerView basado en un tiempo guardado con cada elemento
- Multiline EditText en los problemas de desplazamiento de RecyclerView
- Picasso problema de extracción de imágenes cuando se utiliza con RecyclerView
- RecyclerView GridLayoutManager con el encabezado de ancho completo
- RecyclerView Adapter da error "La jerarquía del tipo RecycleAdapter es inconsistente"
Para tener los elementos cuadrados en mi RecyclerView, proveo un envoltorio simple para mi elemento de vista raíz; Utilizo el siguiente SquareRelativeLayout
en lugar de RelativeLayout
.
package net.simplyadvanced.widget; import android.content.Context; import android.util.AttributeSet; import android.widget.RelativeLayout; /** A RelativeLayout that will always be square -- same width and height, * where the height is based off the width. */ public class SquareRelativeLayout extends RelativeLayout { public SquareRelativeLayout(Context context) { super(context); } public SquareRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(VERSION_CODES.LOLLIPOP) public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Set a square layout. super.onMeasure(widthMeasureSpec, widthMeasureSpec); } }
Entonces, en mi diseño XML para el adaptador, acabo de hacer referencia a la vista personalizada como se muestra a continuación. Sin embargo, puede hacerlo también mediante programación.
<?xml version="1.0" encoding="utf-8"?> <net.simplyadvanced.widget.SquareRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/elementRootView" android:layout_width="wrap_content" android:layout_height="wrap_content"> <!-- More widgets here. --> </net.simplyadvanced.widget.SquareRelativeLayout>
Nota: Dependiendo de la orientación de la cuadrícula, es posible que desee que el ancho se base en la altura ( GridLayoutManager.HORIZONTAL
) en lugar de la altura que se basa en el ancho ( GridLayoutManager.VERTICAL
).
En caso de que alguien quiera escalar la vista de manera diferente – así es como lo haces:
private static final double WIDTH_RATIO = 3; private static final double HEIGHT_RATIO = 4; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = (int) (HEIGHT_RATIO / WIDTH_RATIO * widthSize); int newHeightSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, newHeightSpec); }
El diseño de restricciones resuelve este problema. Usar app:layout_constraintDimensionRatio="H,1:1"
Recyclerview_grid_layout.xml
<android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <ImageView android:id="@+id/imageview" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintDimensionRatio="H,1:1" android:scaleType="centerCrop" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout>
Por favor, pruebe esta extensión de FrameLayout. Realiza una medición doble para mejorar la consistencia. También admite propiedades XML personalizadas para configurar la relación de aspecto necesaria de los diseños
public class StableAspectFrameLayout extends FrameLayout { private int aspectWidth = 1; private int aspectHeight = 1; public StableAspectFrameLayout(Context context) { this(context, null, 0); } public StableAspectFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StableAspectFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); extractCustomAttrs(context, attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public StableAspectFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); extractCustomAttrs(context, attrs); } private void extractCustomAttrs(Context context, AttributeSet attrs) { if (attrs == null) return; TypedArray a = context.getResources().obtainAttributes(attrs, R.styleable.StableAspectFrameLayout); try { aspectWidth = a.getInteger(R.styleable.StableAspectFrameLayout_aspect_width, 1); aspectHeight = a.getInteger(R.styleable.StableAspectFrameLayout_aspect_height, 1); } finally { a.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int newSpecWidth = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY); int newH = Math.round(((float) getMeasuredWidth()) * aspectHeight / aspectWidth); int newSpecHeigh = MeasureSpec.makeMeasureSpec(newH, MeasureSpec.EXACTLY); super.onMeasure(newSpecWidth, newSpecHeigh); } }
Y el contenido de la attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- StableAspectFrameLayout --> <declare-styleable name="StableAspectFrameLayout"> <attr name="aspect_width" format="integer"/> <attr name="aspect_height" format="integer"/> </declare-styleable> </resources>
Una vez más, recomiendo el relativamente reciente 'por ciento' layouts. Usando la dependencia 'com.android.support:percent:25.2.0'
, puedes hacer algo como esto:
<android.support.percent.PercentFrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/image" app:layout_widthPercent="100%" app:layout_aspectRatio="100%" android:padding="10dp" android:scaleType="centerCrop" android:cropToPadding="true" tools:background="#efdbed" /> </android.support.percent.PercentFrameLayout>
Es probablemente mucho más rápido que ConstraintLayout, aunque algún día probablemente no nos importará más.
No me gusta la respuesta elegida, así que permítanme proporcionar la mía: En lugar de envolver el diseño completo de un elemento en SomeDammyLayoutWithFixedAspectRatio, puede cortar GridLayoutManager y volver a escribir el código dentro de measureChild. He reemplazado estas líneas:
if (mOrientation == VERTICAL) { wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, horizontalInsets, lp.width, false); hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(), verticalInsets, lp.height, true); } else { hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, verticalInsets, lp.height, false); wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(), horizontalInsets, lp.width, true); }
a:
if (mOrientation == VERTICAL) { wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, horizontalInsets, lp.width, false); hSpec = wSpec; } else { hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, verticalInsets, lp.height, false); wSpec = hSpec; }
Parece que funciona bien.
No me malinterpreten, esto es bastante complicado también, pero al menos esta solución no perjudica el rendimiento de la aplicación al extender la jerarquía de la vista
- Lista dinámica que agrega "cargar más artículos" al final del desplazamiento
- AsyncTask onPostExecute nunca se llama