22

I create my own class for the square layout:

public class SquareLayout extends LinearLayout{

    public SquareLayout(Context context) {
        super(context);
    }

    public SquareLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public SquareLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


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

Then, in my xml:

...
        <com.myApp.SquareLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="center_horizontal"
            android:orientation="horizontal" >

            <ImageView
                android:id="@+id/cellImageView"
                android:adjustViewBounds="true"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp"
                android:padding="2dp"
                android:scaleType="centerCrop"
                android:src="@drawable/image" />
        </com.myApp.SquareLayout>
...

Nothing written more in my java code. But instead if my layout and my Image, I see only a white rectangle...

What am I wrong?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Geltrude
  • 1,093
  • 3
  • 17
  • 35

3 Answers3

21

// you forget to call super.onMeasure(widthMeasureSpec, widthMeasureSpec);

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    super.onMeasure(widthMeasureSpec, widthMeasureSpec);
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    int size = width > height ? height : width;
    setMeasuredDimension(size, size);

}

// xml file

<com.example.testapplication.SquareLayout
    android:id="@+id/layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center_horizontal"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/cellImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dp"
        android:adjustViewBounds="true"
        android:padding="2dp"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher" />

  </com.example.testapplication.SquareLayout> 
Pawan Yadav
  • 1,772
  • 19
  • 17
  • com.example.testapplication.SquareLayout is fully qualified path of SquareLayout file – Pawan Yadav May 25 '13 at 10:12
  • 1
    Warning: this will not work with a `RelativeLayout` as the base class. I was trying to do exactly this, but with a base class of `RelativeLayout`, and the child with `centerInParent`, and it wasn't appropriately re-laying out the children. With a `LinearLayout` instead, it works perfectly. – karl Nov 15 '13 at 03:49
9

I had problems calling setMeasuredDimension directly when applying the same technique to a RelativeLayout. I was unable to correctly align against the bottom edge. Changing to instead call up to super.onMeasure() with a new measure spec worked better.

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    int size = Math.min(width, height);
    super.onMeasure(MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY),
        MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY));
  }
Alex North
  • 1,189
  • 13
  • 12
  • 3
    This is a much more flexible solution than the accepted answer as it can be used with many different superclasses aside from LinearLayout. – Matej Jul 25 '14 at 13:04
  • All available answers are similar, but have problems with measuring children's size, this is the right answer, thanks a lot. – Farshad Sep 06 '17 at 09:37
6

Edit:

The solution below has now been deprecated, as ConstraintLayout has become the new standard and provides this functionality.

Original Answer:

Turns out the Android team gave us the solution, but nobody knows about it! Check out these two classes from the Percent Support Library:

If you want to impose the ratio of a view, you have to place it within one of these layouts. So in this case, you have to place a standard LinearLayout, not your subclass, within one of these layouts with the right aspect ratio. Example if you want to use a PercentFrameLayout:

<android.support.percent.PercentFrameLayout
         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">
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:layout_aspectRatio="100%">
         <!-- Whatever subviews you want here -->
     </LinearLayout>
 </android.support.percent.PercentFrameLayout>

And there you go, all your views will be contained within a square linear layout!

don't forget to add the gradle dependency compile 'com.android.support:percent:23.3.0' Adjust the version number as required

Martin Devillers
  • 720
  • 8
  • 11
  • 2
    Aspect ratio does not seem to work unless only either width or height is defined and is defined as a fixed number (instead of `match_parent`). Using `com.android.support:percent:25.0.1`. – Pang Nov 25 '16 at 10:43
  • Both PercentFrameLayout and PercentRelativeLayout are deprecated now. ConstraintLayout is recommended to use instead. – dzikovskyy Jul 21 '17 at 22:32
  • use jetpack library implementation "androidx.percentlayout:percentlayout:1.0.0" – Islam Assem Jan 03 '22 at 10:30