Square layout on GridLayoutManager for RecyclerView
To have the square elements in my RecyclerView, I provide a simple wrapper for my root View element; I use the following SquareRelativeLayout
in place of 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);
}
}
Then, in my XML layout for the adapter, I've just referenced the custom view as shown in the following. Though, you can do this programmatically also.
<?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>
Note: Depending on which orientation your grid is, then you may want to have the width based off of height (GridLayoutManager.HORIZONTAL
) instead of the height being based off the width (GridLayoutManager.VERTICAL
).
Constraint layout solves this problem. Use 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="0dp"
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>
EDIT
Set ImageView width to 0dp
. match_parent
is now deprecated for ConstraintLayout.
Starting API 26 (Support Library 26.0), one can use ConstraintLayout that exposes aspect ratio property to force views to be squared: 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
}
Example of layout I'm using in 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"
is the key attribute here
In case someone would like to scale the view differently - this is how you do it:
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);
}