182

I'm trying to make a gridview with two columns. I mean two photos per row side by side just like this image.

enter image description here

But my pictures have spaces between them, due to the fact that it's not the same size. Here is what I'm getting.

enter image description here

as you can see the first picture hides the legend which shows the contact name and phone number. and the other pictures are not stretched correctly.

Here is my GridView xml file. As you can see the columnWidth is set to 200dp. I'd like it to be automatic, so the pictures will resize automatically for each screen size.

<?xml version="1.0" encoding="utf-8"?>
<GridView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridViewContacts"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:numColumns="2"
    android:columnWidth="200dp"
    android:stretchMode="columnWidth"    
    android:gravity="center" />

and here is the item xml file, which represents each item itself.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageViewContactIcon"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />

    <LinearLayout
        android:id="@+id/linearlayoutContactName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingLeft="5dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:background="#99000000"
        android:layout_alignBottom="@+id/imageViewContactIcon">

        <TextView
            android:id="@+id/textViewContactName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:textStyle="bold"
            android:textSize="15sp"
            android:text="Lorem Ipsum" />       

        <TextView
            android:id="@+id/textViewContactNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FFFFFF"
            android:layout_marginLeft="5dp"
            android:focusable="true"
            android:ellipsize="marquee"
            android:marqueeRepeatLimit="marquee_forever"
            android:textSize="10sp"
            android:text="123456789" />

    </LinearLayout>

</RelativeLayout>

So what I want, is to show two images per row, and the images auto resized, no matter the screen size. What am I doing wrong on my layout?

Thanks.

rogcg
  • 10,451
  • 20
  • 91
  • 133
  • 1
    Perhaps you should try encapsulating the ImageView in its own LinearLayout. This way you could configure the LinearLayout tag exactly as you want it and then simply have the ImageView fill it. – dani Mar 07 '13 at 01:05
  • @dani what you mean? i still can configure the ImageView exactly as I want. could you give me some example? – rogcg Mar 07 '13 at 01:17
  • i think u need to create thumbs from images, which are all the same, ex 100x100 or whatever u need them. If u can see on your example image, the effect u looking for is display only part of image. And in your project u just display original image, which is wrong if u need to make gallery. Try looking for thumbs in android dev web site :) – Vasil Valchev Mar 07 '13 at 01:32
  • @VasilValchev but the problem is, even if I create thumbs (e.g. 100x100) I'm gonna need to resize it for different screen sizes. I alredy use the centerCrop `scaleType` property on the ImageView. What I need to achieve is a way to make the pictures side by side, and automatically resize for different screen sizes. – rogcg Mar 07 '13 at 01:42
  • You always can get screen size in horizontal whit code and just divide it by /2. If you can see in your project the problem is in height, if u know what exactly is your weight u always can make a perfect rectangle. I don`t see where the problem is? – Vasil Valchev Mar 07 '13 at 01:55
  • Maybe you are expecting that the grid view will do all the work for u? – Vasil Valchev Mar 07 '13 at 01:56
  • @VasilValchev yes.. I was expecting the grid to do all the work for me. isn't that possible? do I have to manage it programatically? – rogcg Mar 07 '13 at 01:58
  • :) I`m sorry, but good looking images displayed in ImageView need some code – Vasil Valchev Mar 07 '13 at 02:01

2 Answers2

403

Here's a relatively easy method to do this. Throw a GridView into your layout, setting the stretch mode to stretch the column widths, set the spacing to 0 (or whatever you want), and set the number of columns to 2:

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:verticalSpacing="0dp"
        android:horizontalSpacing="0dp"
        android:stretchMode="columnWidth"
        android:numColumns="2"/>

</FrameLayout>

Make a custom ImageView that maintains its aspect ratio:

src/com/example/graphicstest/SquareImageView.java

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);
        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
    }
}

Make a layout for a grid item using this SquareImageView and set the scaleType to centerCrop:

res/layout/grid_item.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <com.example.graphicstest.SquareImageView
        android:id="@+id/picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:paddingBottom="15dp"
        android:layout_gravity="bottom"
        android:textColor="@android:color/white"
        android:background="#55000000"/>

</FrameLayout>

Now make some sort of adapter for your GridView:

src/com/example/graphicstest/MyAdapter.java

private final class MyAdapter extends BaseAdapter {
    private final List<Item> mItems = new ArrayList<Item>();
    private final LayoutInflater mInflater;

    public MyAdapter(Context context) {
        mInflater = LayoutInflater.from(context);

        mItems.add(new Item("Red",       R.drawable.red));
        mItems.add(new Item("Magenta",   R.drawable.magenta));
        mItems.add(new Item("Dark Gray", R.drawable.dark_gray));
        mItems.add(new Item("Gray",      R.drawable.gray));
        mItems.add(new Item("Green",     R.drawable.green));
        mItems.add(new Item("Cyan",      R.drawable.cyan));
    }

    @Override
    public int getCount() {
        return mItems.size();
    }

    @Override
    public Item getItem(int i) {
        return mItems.get(i);
    }

    @Override
    public long getItemId(int i) {
        return mItems.get(i).drawableId;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        View v = view;
        ImageView picture;
        TextView name;

        if (v == null) {
            v = mInflater.inflate(R.layout.grid_item, viewGroup, false);
            v.setTag(R.id.picture, v.findViewById(R.id.picture));
            v.setTag(R.id.text, v.findViewById(R.id.text));
        }

        picture = (ImageView) v.getTag(R.id.picture);
        name = (TextView) v.getTag(R.id.text);

        Item item = getItem(i);

        picture.setImageResource(item.drawableId);
        name.setText(item.name);

        return v;
    }

    private static class Item {
        public final String name;
        public final int drawableId;

        Item(String name, int drawableId) {
            this.name = name;
            this.drawableId = drawableId;
        }
    }
}

Set that adapter to your GridView:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    GridView gridView = (GridView)findViewById(R.id.gridview);
    gridView.setAdapter(new MyAdapter(this));
}

And enjoy the results:

Example GridView

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • 3
    it worked! but I'd like you to explain me the need of creating a class for the `ImageView` and why use something like this `` instead of a simple `` tag in the **xml** file. Could you explain me more about this _aspect ratio_, and how not creating this class, affects the items. is there a way to optimize this, without the need of the custom `ImageView` class? thanks for your help anyways. – rogcg Mar 07 '13 at 16:57
  • 10
    Basically, in Android's ImageView class, there's no way to simply specify "hey, keep a square aspect ratio (width / height) for this view" unless you hard code width and height. You could do some manual adjustment of LayoutParams in the adapter's getView, but frankly, it's much simpler to let ImageView handle all the measurements, and just override the results to say "Whatever the width ends up being, make my height stay the same". You never have to think about it, it's always square, and it just works as expected. Basically this is the easiest way to keep the view square. – Kevin Coppock Mar 07 '13 at 17:51
  • 8
    I can't think of a reason to *avoid* making the custom ImageView class, as you can do everything with it that you can with a regular ImageView, but this just keeps you from having to do measurements and LayoutParams instantiation and checking all over the place. – Kevin Coppock Mar 07 '13 at 17:56
  • i think there is a trivial bug, you must to switch between the two layouts name! i mean in onCreate callback must be set setContentView(R.layout.grid_item) instead of setContentView(R.layout.main) ,, and in MyAdapter class must setContentView(R.layout.main) instead of setContentView(R.layout.grid_item) – Hussam Otri Jul 30 '13 at 00:21
  • @HusamOtri That is definitely incorrect. I'm *inflating* `grid_item` not setting it as the content view. The content view includes the `GridView`. – Kevin Coppock Jul 30 '13 at 03:54
  • Also, the screenshot was taken from the code compiled above. You can try it yourself. – Kevin Coppock Jul 30 '13 at 03:54
  • @kcoppock You have a right, i flipped the names of two layouts in my own example,sorry for this mistake!! – Hussam Otri Aug 02 '13 at 03:39
  • @kcoppock Please help me sir I have used your answer and Created GridView but I am not able to set Click events on it Please Help me My question url is [this](http://stackoverflow.com/questions/20096558/nothings-happening-on-clicking-items-of-gridview/20096903?noredirect=1#comment29971500_20096903) – Android Noob Nov 21 '13 at 05:51
  • hey @rogcg u can just use this answer and for the images make width and height to be `match_parent` and use `android:scaleType="centerCrop"` – Sir NIkolay Cesar The First Jan 07 '14 at 13:45
  • @kcoppock Hi, how can I get which ID click on this GridView? – 502_Geek Feb 21 '14 at 08:24
  • You could merge this code with this example: http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/ – Kevin Coppock Feb 23 '14 at 07:46
  • I have used above code. But am facing a problem. When ever I am trying to display pictures whose size is high For.eg(1920 X 1200), then the activity crashes. I don't know what exactly is happening but. Can you please guide me how can i achieve that. I want to display bigger resolution pictures as thumbnail and then when that is touched it will displayed in fullscreen. – Mayur May 12 '14 at 07:15
  • I've tried this, but the `v = inflater.inflate...` gives an error on `inflater` – Zander Feb 10 '15 at 12:09
  • 1
    Thanks for editing, thought it could be that, working like a charm! – Zander Feb 10 '15 at 15:47
  • 1
    @kcoppock: Thanks a lot for sharing this code. What I like most is that you can replace the colors for the imageview by "real" pictures and the are resized properly! What I would like to understand is if its possible to resize the views so that a certain number of views fill the whole screen. – AntonSack Apr 30 '15 at 08:46
  • Best possible solution to write a custom class. – Srikrishnan Suresh Jul 04 '15 at 16:56
  • When I use `MultiChoiceModeListener` to this grid view,it is not highlighting the selected image,I tried `gridview.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL);` and [this solution](http://stackoverflow.com/a/30680513/4361127) – Prabs Sep 07 '15 at 13:05
  • Solved.. To highlight the selected image and give some color to the background add `android:padding="3dp"` to grid_item – Prabs Sep 08 '15 at 05:14
  • Does the onCreate for the GridView go in the SquareImageView.java file? or a new one? – user2407147 Feb 23 '16 at 12:05
  • @user2407147 Thats intended for your Activity. – Kevin Coppock Feb 23 '16 at 16:03
  • If it extends ImageView, how can you have a onCreate? – user2407147 Feb 23 '16 at 21:26
  • @user2407147 Again, it's for the Activity, not the View. Your ImageView isn't an Activity. This is just showing where you would initialize your adapter. – Kevin Coppock Feb 23 '16 at 21:27
  • NOTE: [Setmax's newer answer](http://stackoverflow.com/a/32666201/199364) seems simpler, for current Android versions. (No longer need to create CustomImageView class.) – ToolmakerSteve Dec 27 '16 at 21:39
  • 1
    If anyone is trying to implement this within a fragment, it reads as @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.main, container, false); GridView gridView = (GridView) rootView.findViewById(R.id.gridview); gridView.setAdapter(new MyAdapter(getActivity())); return rootView; } – Iorek Jan 19 '17 at 04:19
  • for some reason, when I'm trying to make the SquareImageView class, it's asking me to change `ImageView` to `android.support.v7.widget.AppCompatImageView`. Is there a way I can use normal ImageView ? – mrid Sep 18 '17 at 07:22
  • @mrid just use AppCompatImageView as suggested if you're using the support library. – Kevin Coppock Sep 18 '17 at 13:46
14

another simple approach with modern built-in stuff like PercentRelativeLayout is now available for new users who hit this problem. thanks to android team for release this item.

<android.support.percent.PercentRelativeLayout 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:clickable="true"
app:layout_widthPercent="50%">

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#55000000"
        android:paddingBottom="15dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="15dp"
        android:textColor="@android:color/white" />

</FrameLayout>

and for better performance you can use some stuff like picasso image loader which help you to fill whole width of every image parents. for example in your adapter you should use this:

int width= context.getResources().getDisplayMetrics().widthPixels;
    com.squareup.picasso.Picasso
            .with(context)
            .load("some url")
            .centerCrop().resize(width/2,width/2)
            .error(R.drawable.placeholder)
            .placeholder(R.drawable.placeholder)
            .into(item.drawableId);

now you dont need CustomImageView Class anymore.

P.S i recommend to use ImageView in place of Type Int in class Item.

hope this help..

Setmax
  • 946
  • 6
  • 10
  • With API 26.1 this got depricated. See [link](https://developer.android.com/reference/android/support/percent/PercentRelativeLayout.html) – Dominikus K. Nov 10 '17 at 15:37