web-dev-qa-db-fra.com

Disposition carrée sur GridLayoutManager pour RecyclerView

J'essaie de faire une mise en page avec des images carrées. J'ai pensé qu'il devait être possible de manipuler le GridLayoutManager en manipulant onMeasure pour faire un

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

au lieu de

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

mais malheureusement, cela n'a pas fonctionné.

Des idées?

58
Paul Woitaschek

Pour avoir les éléments carrés dans RecyclerView, je fournis un wrapper simple pour mon élément racine View; J'utilise SquareRelativeLayout à la place 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);
    }

}

Ensuite, dans ma mise en page XML pour l'adaptateur, je viens de référencer la vue personnalisée, comme illustré ci-dessous. Bien que vous puissiez le faire par programme également.

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

Remarque: En fonction de l’orientation de votre grille, vous souhaiterez peut-être que la largeur soit basée sur la hauteur (GridLayoutManager.HORIZONTAL) au lieu que la hauteur soit basée sur la largeur (GridLayoutManager.VERTICAL).

150
Anonsage

La disposition des contraintes résout ce problème. Utilisation 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>
53
veritas1

Si quelqu'un souhaite redimensionner la vue différemment, procédez comme suit:

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

À partir de l'API 26 (Support Library 26.0), vous pouvez utiliser ConstraintLayout qui expose la propriété de rapport de format pour forcer le quadrillage des vues: 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
}

Exemple de mise en page que j'utilise dans 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" est l'attribut clé ici

7
vir us

S'il vous plaît, essayez cette extension de FrameLayout. Il effectue une double mesure pour améliorer la cohérence. Il prend également en charge les propriétés XML personnalisées pour configurer les rapports d’aspect requis à partir de mises en page.

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

Et le contenu 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>
3

Encore une fois, je recommande les dispositions relativement récentes de "pourcentage". Utiliser la dépendance 'com.Android.support:percent:25.2.0', tu peux faire quelque chose comme ça:

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

C'est probablement beaucoup plus rapide que ConstraintLayout, même si un jour nous ne nous en soucierons probablement plus.

1
androidguy

Je n'aime pas la réponse choisie, alors laissez-moi vous donner le mien: au lieu d'envelopper la présentation de tout l'élément dans SomeDammyLayoutWithFixedAspectRatio, vous pouvez pirater GridLayoutManager et réécrire le code dans measureChild. J'ai remplacé ces lignes:

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

à:

if (mOrientation == VERTICAL) {
        wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
                horizontalInsets, lp.width, false);
        hSpec = wSpec;
    } else {
        hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode,
                verticalInsets, lp.height, false);
        wSpec = hSpec;
    }

Cela semble marcher correctement.

Ne vous méprenez pas, c'est assez compliqué aussi, mais au moins cette solution ne nuit pas aux performances des applications en étendant la hiérarchie des vues.

0
Karma Maker

J'ai eu un problème similaire et j'ai dû gonfler la vue qui serait carré dans Grid of recycler view. Ci-dessous ma façon de le faire.

Dans la méthode onCreateViewHolder, j'ai utilisé ViewTreeObserver et GlobalLayoutListener pour obtenir la largeur mesurée de la présentation. La mise en page a une valeur match_parent dans l'attribut width. Toute vue de mon recycleur a une disposition au centre 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);
        }
    });

image de référence

0
user8646587