I'm using a GridView
to display a bunch of views which are essentially LinearLayouts
. I want the LinearLayouts
to all be square, but I also want them to be dynamically sized--that is, there are two columns and I want the LinearLayouts
to stretch depending on the size of the screen but remain square. Is there a way to do this through the xml
layout or do I have to set the heights and widths programmatically?

- 9,113
- 13
- 65
- 78

- 13,588
- 9
- 39
- 60
14 Answers
A neat solution for square GridView
items is to extend RelativeLayout
or LinearLayout
and override onMeasure
like so:
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}

- 9,113
- 13
- 65
- 78

- 3,658
- 3
- 21
- 20
-
2This is using the measure spec rather than using setMeasuredDimension() so that is good. But it's not very flexible since it always takes the width. – Adam Dec 18 '13 at 01:22
-
3As @DavidMerriman said above, if the view is wider than it is tall, this will cause the view to extend below the viewing area, cutting off part of the view. – vcapra1 Jun 18 '15 at 18:08
-
7This would be more flexible if it passed the value of Math.min(widthMeasureSpec, heightMeasureSpec) into both arguments of super.onMeasure() – Paul Wintz Aug 19 '17 at 05:19
-
This will work easily with activities but it won't work with an app widget. – Apollo-Roboto Jun 26 '20 at 18:19
With the new ConstraintLayout introduced in Android Studio 2.3, it is now quite easy to build responsive layouts.
In a parent ConstraintLayout, to make any of its children view/layout dynamically square, add this attribute
app:layout_constraintDimensionRatio="w,1:1"
w is to specify width-wise constraints and 1:1 ratio ensures square layout.

- 4,474
- 40
- 55

- 1,181
- 7
- 10
-
12Make sure to set "0dp" for layout width and height in child layout. Otherwise it won't work. – thilina Kj Oct 10 '18 at 18:10
-
It also works for the view itself, I mean if you want a contrantLayout's child to be square, you can define the layout_constraintDimensionRatio attribute directly in the child view – Plinio.Santos Sep 25 '19 at 19:34
-
1Thank you very much for the solution; I've made if it helps a complete gist of my code using your solution: https://gist.github.com/nadar71/006699e31ef7451813e6c97dacfbc5e2 – android_dev71 Jun 11 '20 at 08:52
I've done this way:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int size;
if(widthMode == MeasureSpec.EXACTLY && widthSize > 0){
size = widthSize;
}
else if(heightMode == MeasureSpec.EXACTLY && heightSize > 0){
size = heightSize;
}
else{
size = widthSize < heightSize ? widthSize : heightSize;
}
int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(finalMeasureSpec, finalMeasureSpec);
}
With this implementation, your layout will be square, assuming the lower size between width and height. And it can even be set with dynamic values, like using weight inside a LinearLayout.

- 25,966
- 23
- 76
- 87

- 3,085
- 4
- 30
- 49
-
the method "onMeasure" is to be overrided in a class that extend GridView ? – An-droid May 22 '14 at 09:39
-
Tt's overriden in the class you want it to be square. For the case of the question, it's the LinearLayouts inside the GridView. – Fernando Camargo Sep 23 '14 at 19:46
-
This one helped me fix some strange issues in using a square view in a StaggeredGridLayout. My more naive implementation was not good enough. Thanks! – Peterdk Aug 12 '16 at 22:15
There's nothing in the xml that will let you link the width and height properties. Probably the easiest thing to do is to subclass LinearLayout
and override onMeasure
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = width > height ? height : width;
setMeasuredDimension(size, size);
}
I've used this to create views that are always square before. It should still work for a LinearLayout
.
More info that will help doing this: http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html

- 6,056
- 1
- 18
- 18
-
1If this worked for you, could you mark this as the correct answer? – David Merriman Jan 24 '12 at 20:36
-
Well, I arrived at a solution that while similar, is a little different from this, and I couldn't get it work with onMeasure, so I'm not sure that this code snippet would have worked for me. – Catherine Jan 25 '12 at 10:37
-
1@Catherine hey Catherine, can you post your snippet? Maybe it'd be useful for others :-) – Marek Sebera Jun 18 '13 at 11:38
-
@MarekSebera In retrpospect, that probably would have been a good idea... but it's been a very long time and I'm no longer even working with that codebase! – Catherine Jun 18 '13 at 18:13
-
3Don't forgot to add : `super.onMeasure(widthMeasureSpec, widthMeasureSpec);` ! – dekajoo Oct 05 '14 at 13:15
-
1@deKajoo Using `super.onMeasure(widthMeasureSpec, widthMeasureSpec);` does give you a square, but it's always width x width. If the measurements given are wider than they are tall, you're going to have a problem. My solution uses the smaller of the two measurements, so that you always get the largest square that will fit within the given rectangle. – David Merriman Oct 06 '14 at 19:25
-
Without a call to `super.onMeasure()` child views won't be laid out properly. – nmw May 05 '19 at 09:10
-
Downvote for the ternary operator. Take the Math.min of the two values instead, I'd say. – Mathieu K. May 14 '19 at 16:40
We can do it with a very simple way - just call super.onMeasure()
twice.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int squareLen = Math.min(width, height);
super.onMeasure(
MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY));
}
By calling super.onMeasure()
twice, this is less efficient in terms of the drawing process, but it is a simple way to fix layout issues that the other answers can cause.

- 3,665
- 1
- 22
- 29

- 522
- 5
- 8
-
2instead of calling super.onMeasure twice, the second time you only need to call setMeasuredDimension(squareLen, squareLen); – user3802077 Nov 15 '16 at 18:51
-
Calling `super.onMeasure()` twice makes sure that the layout is done correctly given the new size of the view. Without this we need to trigger a layout in a different way or deal with incorrect layouts. – Richard Le Mesurier Feb 28 '18 at 08:19
It is as simple as:
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);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (widthMeasureSpec < heightMeasureSpec)
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
else
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}

- 3,614
- 4
- 34
- 56
Add the following line in XML:
app:layout_constraintDimensionRatio="1:1"

- 830
- 7
- 9
-
1I wanted to post the same answer because it's better than app:layout_constraintDimensionRatio="w,1:1" as it always keeps ratio if you change configuration – Alexey Simchenko Oct 08 '20 at 12:44
Here's a solution that works for all layout parameters that can be set to view or viewgroup:
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthDesc = MeasureSpec.getMode(widthMeasureSpec);
int heightDesc = MeasureSpec.getMode(heightMeasureSpec);
int size = 0;
if (widthDesc == MeasureSpec.UNSPECIFIED
&& heightDesc == MeasureSpec.UNSPECIFIED) {
size = DP(defaultSize); // Use your own default size, in our case
// it's 125dp
} else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED)
&& !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) {
//Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0)
size = width > height ? width : height;
} else {
//In all other cases both dimensions have been specified so we choose the smaller of the two
size = width > height ? height : width;
}
setMeasuredDimension(size, size);
Cheers

- 8,555
- 3
- 34
- 44
-
This answer has some interesting characteristics and is potentially more robust, but needs work and should construct measure specs which get passed to super.onMeasure() rather than setMeasuredDimension() as it would allow for a variety of super classes. – Adam Dec 18 '13 at 01:27
My suggestion is to create a custom layout class that inherits from FrameLayout. Override the OnMeasure() method and put whatever control you want to be square inside that SquareFrameLayout.
This is how it's done in Xamarin.Android:
public class SquareFrameLayout : FrameLayout
{
private const string _tag = "SquareFrameLayout";
public SquareFrameLayout(Android.Content.Context context):base(context) {}
public SquareFrameLayout(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer):base(javaReference, transfer) {}
public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs):base(context, attrs) {}
public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr):base(context, attrs, defStyleAttr) {}
public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes):base(context, attrs, defStyleAttr, defStyleRes) {}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
int widthSize = MeasureSpec.GetSize(widthMeasureSpec);
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
int heightSize = MeasureSpec.GetSize(heightMeasureSpec);
int width, height;
switch (widthMode)
{
case MeasureSpecMode.Exactly:
width = widthSize;
break;
case MeasureSpecMode.AtMost:
width = Math.Min(widthSize, heightSize);
break;
default:
width = 100;
break;
}
switch (heightMode)
{
case MeasureSpecMode.Exactly:
height = heightSize;
break;
case MeasureSpecMode.AtMost:
height = Math.Min(widthSize, heightSize);
break;
default:
height = 100;
break;
}
Log.Debug(_tag, $"OnMeasure({widthMeasureSpec}, {heightMeasureSpec}) => Width mode: {widthMode}, Width: {widthSize}/{width}, Height mode: {heightMode}, Height: {heightSize}/{height}");
var size = Math.Min(width, height);
var newMeasureSpec = MeasureSpec.MakeMeasureSpec(size, MeasureSpecMode.Exactly);
base.OnMeasure(newMeasureSpec, newMeasureSpec);
}
}
If you want a View (or any other control) to be square (and centered) just add it to your layout the following way:
<your.namespace.SquareFrameLayout
android:id="@+id/squareContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<View
android:id="@+id/squareContent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</your.namespace.SquareFrameLayout>

- 303
- 1
- 2
- 9
Check out SquareLayout, an Android Library which provides a wrapper class for different Layouts, rendering them Squared dimensioned without losing any core functionalities.
The dimensions are calculated just before the Layout is rendered, hence there is no re-rendering or anything as such to adjust once the View is obtained.
To use the Library, add this to your build.gradle:
repositories {
maven {
url "https://maven.google.com"
}
}
dependencies {
compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3'
}
The one you require is SquareLinearLayout.

- 6,733
- 9
- 31
- 60
-
Doesn't work for me on real device :( Rendering well on Android Studio preview, but is not working on device – Eugene Voronoy Mar 03 '20 at 22:34
For anyone wants solution With Kotlin, here's what I did with FrameLayout
.
package your.package.name
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
class SquareLayout: FrameLayout {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
if (widthMeasureSpec < heightMeasureSpec)
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
else
super.onMeasure(heightMeasureSpec, heightMeasureSpec)
}
}

- 540
- 7
- 7
Try this code:
public class SquareRelativeLayout extends RelativeLayout {
public SquareRelativeLayout(Context context) {
super(context);
}
public SquareRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int size;
if (widthMode == MeasureSpec.EXACTLY && widthSize > 0) {
size = widthSize;
} else if (heightMode == MeasureSpec.EXACTLY && heightSize > 0) {
size = heightSize;
} else {
size = widthSize < heightSize ? widthSize : heightSize;
}
int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(finalMeasureSpec, finalMeasureSpec);
}
}

- 6,230
- 7
- 56
- 82
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>

- 5,041
- 4
- 39
- 46
Create a custom layout
import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout
class SquareLayout : LinearLayout {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val size = MeasureSpec.getSize(widthMeasureSpec)
setMeasuredDimension(size, size)
}
}

- 45
- 2
- 6