uso tutorial studio recyclerview example ejemplo cardview card and android android-recyclerview gridlayoutmanager

example - recyclerview tutorial android studio



DiseƱo cuadrado en GridLayoutManager para RecyclerView (9)

Intento hacer un diseño de cuadrícula con imágenes cuadradas. Pensé que debía 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?


A partir de la API 26 (Biblioteca de soporte 26.0), se puede usar ConstraintLayout que expone la propiedad de relación de aspecto para forzar las vistas al cuadrado: https://developer.android.com/training/constraint-layout/index.htm

android { compileSdkVersion 26 buildToolsVersion ''26.0.2'' ... } ... dependencies { compile ''com.android.support:appcompat-v7:26.0.2'' compile ''com.android.support.constraint:constraint-layout:1.1.0-beta1'' //use whatever version is current }

Ejemplo de diseño que estoy usando en GridLayoutManager:

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="@dimen/margin_small" android:background="@drawable/border_gray" android:gravity="center"> <android.support.constraint.ConstraintLayout android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="h,1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <!-- place your content here --> </android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

app:layout_constraintDimensionRatio="h,1:1" es el atributo clave aquí


El diseño de restricciones resuelve este problema. Use la 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>


En caso de que alguien quiera escalar la vista de manera diferente, así es como lo hace:

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); }


No me gusta la respuesta elegida, así que déjame proporcionar la mía: en lugar de envolver el diseño completo del elemento en SomeDammyLayoutWithFixedAspectRatio, puedes hackear 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 también es bastante complicado, pero al menos esta solución no perjudica el rendimiento de la aplicación al extender la jerarquía de vistas


Para tener los elementos cuadrados en mi RecyclerView, proporciono un contenedor simple para mi elemento de vista raíz; Yo uso 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); } }

Luego, en mi diseño XML para el adaptador, acabo de hacer referencia a la vista personalizada como se muestra a continuación. Sin embargo, también puedes hacer esto 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: Según la orientación de la cuadrícula, es posible que desee tener el ancho basado en la altura ( GridLayoutManager.HORIZONTAL ) en lugar de que la altura se base en el ancho ( GridLayoutManager.VERTICAL ).


Por favor, intente esta extensión de FrameLayout. Realiza mediciones dobles para mejorar la consistencia. También admite propiedades XML personalizadas para configurar la relación de aspecto requerida a partir de 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 los atributos.

<?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>


Tuve un problema similar y tuve que inflar la vista que sería cuadrada en la cuadrícula de la vista de reciclador. A continuación se muestra mi forma de hacerlo.

Dentro del método onCreateViewHolder utilicé ViewTreeObserver y GlobalLayoutListener para obtener el ancho medido del diseño. El diseño tiene un valor match_parent en el atributo ancho. Cualquier vista de mi reciclador tiene un diseño en el centro horizontal.

final View view = LayoutInflater.from(mActivity).inflate(R.layout.list_item_deals, parent, false); view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int side = view.getMeasuredWidth(); ViewGroup.LayoutParams lp = view.getLayoutParams(); lp.width = side; lp.height = side; view.setLayoutParams(lp); } });

imagen de referencia


Una pequeña actualización para ConstraintLayout para androidx.

Incluya esta línea en su build.gradle:

implementation ''androidx.constraintlayout:constraintlayout:2.0.0-beta2''

Quería obtener un RecycleView con GridLayoutManager con CardViews cuadrados y utilicé ese diseño para los elementos:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp" > <androidx.cardview.widget.CardView android:id="@+id/cardView" android:layout_width="0dp" android:layout_height="0dp" card_view:cardElevation="4dp" app:layout_constraintDimensionRatio="H,1:1" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" >

En la restricción Diseño

  • layout_width = " match_parent " es importante para permitir que el elemento ocupe tanto espacio como RecyclerView proporciona
  • layout_height = " wrap_content " no permite que el elemento llene toda la altura dada por RecyclerView, sino que usa la altura restringida, proporcionada por ConstraintLayout. En mi caso, cuando usé FrameLayout o LinearLayout, los elementos eran "altos".

En el nodo hijo, en mi caso CardView

  • limitar el tamaño a cero es importante: layout_width = "0dp" y layout_height = "0dp" significa que el ancho y la altura están contravenidos
  • layout_constraintDimensionRatio = "H, 1: 1" produce el efecto deseado, al configurar H, usted define que la altura se limitará 1: 1 es la relación.

Vea algunas explicaciones detalladas en el offsite .


Una vez más, recomiendo los diseños de ''porcentaje'' relativamente recientes. Usando la dependencia ''com.android.support:percent:25.2.0'' , puede 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>

Probablemente sea mucho más rápido que ConstraintLayout, aunque algún día probablemente ya no nos importe.