76

I have a GridView with ImageViews inside. I have 3 of them for each row. I can set correctly the width with WRAP_CONTENT and scaleType = CENTER_CROP, but I don't know how to set the ImageView's size to be a square. Here's what I did until now, it seems to be ok except the height, that is "static":

imageView = new ImageView(context);     
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(GridView.LayoutParams.WRAP_CONTENT, 300));

I'm doing it inside an adapter.

Andrea
  • 4,262
  • 4
  • 37
  • 56

10 Answers10

170

The best option is to subclass ImageView yourself, overriding the measure pass:

public class SquareImageView  extends ImageView {

  ...

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int width = getMeasuredWidth();
    setMeasuredDimension(width, width);
  }

  ...

}
a.bertucci
  • 12,142
  • 2
  • 31
  • 32
  • If I set padding // margin, I get a white border.. Any idea how to fix that? – Waza_Be Aug 04 '13 at 13:25
  • 2
    android:adjustViewBounds="true" android:scaleType="centerCrop" @Waza_Be – Kaloyan Roussev Mar 05 '14 at 04:55
  • 2
    Shouldn't it be set as the min(width,height) instead? – android developer Jun 09 '15 at 12:31
  • 1
    It's a tiny optimization, but checking that the width and the height are different could prevent another measurement pass. @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); int height = getMeasuredHeight(); // Optimization so we don't measure twice unless we need to if (width != height) { setMeasuredDimension(width, width); } } – Chantell Osejo Sep 19 '15 at 00:00
  • Answer is explained in this page http://codesofjunaid.blogspot.com/2017/04/android-responsive-square-image-view.html – Pullat Junaid Aug 13 '18 at 06:39
102

The other answer works fine. This is just an extension of bertucci's solution to make an ImageView with square width and height with respect to xml inflated layout.

Create a class, say a SquareImageView extending ImageView like this,

public class SquareImageView extends ImageView {

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width = getMeasuredWidth();
        setMeasuredDimension(width, width);
    }

}

Now, in your xml do this,

        <com.packagepath.tothis.SquareImageView
            android:id="@+id/Imageview"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

If you need an ImageView not to be dynamically created in program, instead, to be fixed in xml, then this implementation will be helpful.

Andro Selva
  • 53,910
  • 52
  • 193
  • 240
  • 1
    Awesome, thank you Andro. This is precisely what I was looking for. Again, thanks for taking the time to provide a complete answer. – Taliadon Oct 24 '13 at 17:16
  • 1
    This should be marked as the correct, full solution. – Jarrod Robins Jul 30 '14 at 00:41
  • See also a good tutorial that uses this technique: https://www.raywenderlich.com/2945946-glide-tutorial-for-android-getting-started – Mr-IDE Jan 15 '22 at 18:32
27

Even more simple:

public class SquareImageView extends ImageView {
    ...
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
    }    
}
mixel
  • 25,177
  • 13
  • 126
  • 165
  • don't forget for sdk >= 21 @TargetApi(Build.VERSION_CODES.LOLLIPOP) public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } – Jan Rabe Mar 14 '16 at 12:35
11

Several of the previous answers are perfectly sufficient. I'm just adding a small optimization to both @Andro Selva and @a.bertucci's solutions here:

It's a tiny optimization, but checking that the width and the height are different could prevent another measurement pass.

public class SquareImageView extends ImageView {

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();

        // Optimization so we don't measure twice unless we need to
        if (width != height) {
            setMeasuredDimension(width, width);
        }
    }

}
Chantell Osejo
  • 1,456
  • 15
  • 25
3

For those looking for a Kotlin solution:

class SquareImageView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
) : ImageView(context, attrs, defStyleAttr, defStyleRes){
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) = super.onMeasure(widthMeasureSpec, widthMeasureSpec)
}
3

An squareImageView by specified width:

public class SquareImageViewByWidth extends AppCompatImageView {

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

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

    public SquareImageViewByWidth(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width= getMeasuredWidth();
        setMeasuredDimension(width, width);
    }

     ...
}

An squareImageView by specified height:

    public class SquareImageViewByHeight extends AppCompatImageView {

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

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

        public SquareImageViewByHeight(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);

            int height = getMeasuredHeight();
            setMeasuredDimension(height, height);
        }

        ...
    }

An squareImageView by minimum of dimentions:

public class SquareImageViewByMin extends AppCompatImageView {

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

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

        public SquareImageViewByHeight(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            int minSize = Math.min(width, height);
            setMeasuredDimension(minSize, minSize);
        }

       ...
    }
Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
Misagh
  • 3,403
  • 1
  • 20
  • 17
1

if someone wants the view to be not square, but proportionally resized in height (for example, 16/9 or 1/3), you can do it like this:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth()/3, MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
John
  • 1,447
  • 15
  • 16
0

Single line solution -

layout.setMinimumHeight(layout.getWidth());
HarshitG
  • 2,677
  • 3
  • 16
  • 13
0

Fast solution for Kotlin (Material.ShapeableImageView can be changed to ImageView)

class SquareImageView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ShapeableImageView(context, attrs, defStyleAttr) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(measuredWidth, measuredWidth)
    }
}
-1

Here all are unnecessary calling its superclass for onMeasure. Here is my implementation

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    int size = Math.min(width, height);
    setMeasuredDimension(size, size);
}
jnovack
  • 7,629
  • 2
  • 26
  • 40
Sourav Bagchi
  • 656
  • 7
  • 13