Making GridView items square

AndroidGridview

Android Problem Overview


I would like the items of my GridView to be square. There are 2 columns and the items width is fill_parent (e.g. they take as much horizontal space as possible. The items are custom views.

How do I make the items height to be equal to their variable width?

Android Solutions


Solution 1 - Android

There is a simpler solution when GridView columns are stretched. Just override the onMeasure of the GridView item layout with...

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

Solution 2 - Android

Or if you want to extend a View just do something really simple like:

public class SquareImageView extends ImageView
{

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

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

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


    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
    {
        final int width = getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec);
        setMeasuredDimension(width, width);
    }

    @Override
    protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh)
    {
        super.onSizeChanged(w, w, oldw, oldh);
    }
}

It's worth noting that super.onMeasure() is not needed. onMeasured requirement is that you must call setMeasuredDimension.

Solution 3 - Android

I'm not sure this is possible with the current widgets. Your best bet might be to put your custom view in a custom "SquareView". This view could just contain 1 child view, and force the height to equal the width when its onLayout method is called.

I never tried to do something like that, but I think it shouldn't be too difficult. An alternative (and maybe slightly easier) solution might be to subclass your custom view's root layout (like, if it's a LinearLayout, make a SquareLinearLayout), and use that as a container instead.

edit : Here's a basic implementation which seems to work for me :

package com.mantano.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

public class SquareView extends ViewGroup {

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

    @Override
    protected void onLayout(boolean changed, int l, int u, int r, int d) {
        getChildAt(0).layout(0, 0, r-l, d-u); // Layout with max size
    }

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

        View child = getChildAt(0);
        child.measure(widthMeasureSpec, widthMeasureSpec);
        int width = resolveSize(child.getMeasuredWidth(), widthMeasureSpec);
        child.measure(width, width); // 2nd pass with the correct size
        setMeasuredDimension(width, width);
    }
}

It's designed to have a unique child, but I ignored all of the checks for the sake of simplicity. The basic idea is to measure the child with the width/height parameters set by the GridView (in my case, it uses numColumns=4 to calculate the width), and then do a second pass with the final dimensions, with height=width... The layout is just a plain layout, which layouts the unique child at (0, 0) with the desired dimensions (right-left, down-up).

And here's the XML used for the GridView items :

<?xml version="1.0" encoding="utf-8"?>
<com.mantano.widget.SquareView
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="wrap_content">

	<LinearLayout android:id="@+id/item" android:layout_width="fill_parent"
		android:layout_height="fill_parent" android:orientation="horizontal"
		android:gravity="center">

		<TextView android:id="@+id/text" android:layout_width="fill_parent"
			android:layout_height="wrap_content" android:text="Hello world" />
	</LinearLayout>
</com.mantano.widget.SquareView>

I used a LinearLayout inside the SquareView in order to have it manage all the gravity possibilities, margins, etc.

I'm not sure how well (or bad) this widget would react to orientation and dimension changes, but it seems to work correctly.

Solution 4 - Android

It ends up being fairly easy to get the grid item square.

In your "GetView" method of your grid adapter, just do:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    GridView grid = (GridView)parent;
    int size = grid.getRequestedColumnWidth();

    TextView text = new TextView(getContext());
    text.setLayoutParams(new GridView.LayoutParams(size, size));

    // Whatever else you need to set on your view

    return text;
}

Solution 5 - Android

I don`t know if it is the best way, but I accomplish that making my custom view to override onSizeChanged that way:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
	super.onSizeChanged(w, h, oldw, oldh);
	
	if (getLayoutParams() != null && w != h) {
		getLayoutParams().height = w;
		setLayoutParams(getLayoutParams());
	}
}

I use this custom view inside a LinearLayout and a GridView and in both it is showing square!

Solution 6 - Android

This worked for me!

If you are like me and you can't use getRequestedColumnWidth() because your minSdk is lower than 16, I suggest this option.

  1. in your fragment:

    DisplayMetrics dm = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); int size = dm.widthPixels / nbColumns; CustomAdapter adapter = new CustomAdapter(list, size, getActivity());

  2. in your adapter (in getView(int position, View convertView, ViewGroup parent))

    v.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, size));

  3. fragment.xml:

    <GridView android:id="@+id/gridView" android:layout_width="match_parent" android:layout_height="match_parent" android:horizontalSpacing="3dp" android:verticalSpacing="3dp" android:numColumns="4" android:stretchMode="columnWidth" />

  4. custom_item.xml: (for example)

    <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/picture" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" />

Hope it will help someone!

Solution 7 - Android

if you set a static number of columns in the xml then you can take the width of the view and divide it by the number of columns.

if you are using auto_fit then it's going to be a bit tricky to get the column count ( there is no getColumnCount() method ), btw this question should help you somehow.

this is the code I'm using into the getView(...) method of the adapter with a fixed number of columns:

    item_side = mFragment.holder.grid.getWidth() / N_COL;
    v.getLayoutParams().height = item_side;
    v.getLayoutParams().width = item_side;

Solution 8 - Android

Try

<GridView
...
android:stretchMode="columnWidth" />

You can also play with other modes

Solution 9 - Android

This is what I am doing to show square cells in a GridView. I am not sure if you want square cells or something else.

GridView grid = new GridView( this );
grid.setColumnWidth( UIScheme.cellSize );
grid.setVerticalSpacing( UIScheme.gap );
grid.setStretchMode( GridView.STRETCH_COLUMN_WIDTH );
grid.setNumColumns( GridView.AUTO_FIT );

And then the view which I am creating for each cell I have to do the following to it:

cell.setLayoutParams( new GridView.LayoutParams(iconSize, iconSize) );

Solution 10 - Android

Based on SteveBorkman's answer, which is API 16+:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    GridView grid = (GridView)parent;
    int size = grid.getColumnWidth();

    if (convertView == null){
        convertView = LayoutInflater.from(context).inflate(R.layout.your_item, null);
        convertView.setLayoutParams(new GridView.LayoutParams(size, size));
    }

     //Modify your convertView here
    
     return convertView;
}

Solution 11 - Android

Along the lines of @Gregory's answer, I had to wrap my image in a LinearLayout in order to keep GridView from manipulating its dimensions from square. Note that the outer LinearLayout should be set to have the dimension of the image, and the width of the GridView column should be the width of the image PLUS any margin. For example:

    <!-- LinearLayout wrapper necessary to wrap image button in order to keep square;
    otherwise, the grid view distorts it into rectangle.

    also, margin top and right allows the indicator overlay to extend top and right.

    NB: grid width is set in filter_icon_page.xml, and must correspond to size + margin
    -->
<LinearLayout
        android:id="@+id/sport_icon_lay"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:orientation="vertical"

        android:layout_marginRight="6dp"
        android:layout_marginTop="6dp"
        >

    <ImageButton
            android:id="@+id/sport_icon"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:maxHeight="60dp"
            android:maxWidth="60dp"
            android:scaleType="fitCenter"
            />
</LinearLayout>

and the GridView like:

   <GridView
        android:id="@+id/grid"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:fillViewport="true"

        android:columnWidth="66dp"

        android:numColumns="auto_fit"
        android:verticalSpacing="25dp"
        android:horizontalSpacing="24dp"
        android:stretchMode="spacingWidth"

        />

Solution 12 - Android

In your activity

 DisplayMetrics displaymetrics = new DisplayMetrics();
 getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
 valX = displaymetrics.widthPixels/columns_number;

in the CustomAdapter of the GridView

 v.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT, YourActivity.valX));

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanagafView Question on Stackoverflow
Solution 1 - AndroidjdamcdView Answer on Stackoverflow
Solution 2 - AndroidChris.JenkinsView Answer on Stackoverflow
Solution 3 - AndroidGregoryView Answer on Stackoverflow
Solution 4 - AndroidSteveBorkmanView Answer on Stackoverflow
Solution 5 - AndroidLuckcheeseView Answer on Stackoverflow
Solution 6 - AndroidAnne-ClaireView Answer on Stackoverflow
Solution 7 - AndroidMario LenciView Answer on Stackoverflow
Solution 8 - AndroidArtView Answer on Stackoverflow
Solution 9 - AndroidSileriaView Answer on Stackoverflow
Solution 10 - AndroidJoaquin IurchukView Answer on Stackoverflow
Solution 11 - Androidlarham1View Answer on Stackoverflow
Solution 12 - AndroidBoris KarloffView Answer on Stackoverflow