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

 super.onMeasure(recycler, state, widthSpec, heightSpec); 

Pero desafortunadamente, eso no funcionó.

¿Algunas ideas?

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

  • Cómo permanecer en una posición de desplazamiento en RecyclerView después de añadir elementos en su primer índice y llamar a notificydatasetchange
  • Uso de CardView y RecyclerView en mis archivos de diseño genera una excepción
  • Almacenamiento de gifs en fresco
  • RecyclerView scrollToPosition no dispara scrollListener
  • Tipos incompatibles: no se puede emitir el visor a imageviewholder
  • Múltiples elementos seleccionados RecyclerView in Activity.java
  • Haga doble clic en OnClickListener de un adaptador de vista de reciclador
  • Optimización de RecyclerView / ListView
  • Vista de reciclador - cambia el tamaño de la vista de elemento mientras se desplaza (para efecto de carrusel)
  • Cómo utilizar RecyclerView.scrollToPosition () para mover la posición a la parte superior de la vista actual?
  • Android Cómo reciclar el mapa de bits correctamente cuando se utiliza RecyclerView?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.