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?
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
).
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>
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);
}
À 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
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>
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.
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.
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);
}
});