Gridview with two columns and auto resized images
Asked Answered
S

2

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.

Seppuku answered 7/3, 2013 at 0:47 Comment(8)
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.Ani
@Ani what you mean? i still can configure the ImageView exactly as I want. could you give me some example?Seppuku
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 :)Watchmaker
@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.Seppuku
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?Watchmaker
Maybe you are expecting that the grid view will do all the work for u?Watchmaker
@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?Seppuku
:) I`m sorry, but good looking images displayed in ImageView need some codeWatchmaker
A
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

Ardellaardelle answered 7/3, 2013 at 5:49 Comment(26)
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 <com.example.graphicstest.SquareImageView> instead of a simple <ImageView> 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.Seppuku
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.Ardellaardelle
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.Ardellaardelle
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)Catalinacatalo
@HusamOtri That is definitely incorrect. I'm inflating grid_item not setting it as the content view. The content view includes the GridView.Ardellaardelle
Also, the screenshot was taken from the code compiled above. You can try it yourself.Ardellaardelle
@kcoppock You have a right, i flipped the names of two layouts in my own example,sorry for this mistake!!Catalinacatalo
@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 thisCholeric
hey @Seppuku u can just use this answer and for the images make width and height to be match_parent and use android:scaleType="centerCrop"Gerdes
@kcoppock Hi, how can I get which ID click on this GridView?Barcot
You could merge this code with this example: curious-creature.org/2012/12/11/…Ardellaardelle
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.Maladjustment
I've tried this, but the v = inflater.inflate... gives an error on inflaterDevanagari
Thanks for editing, thought it could be that, working like a charm!Devanagari
@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.Chrisom
Best possible solution to write a custom class.Societal
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 solutionYogh
Solved.. To highlight the selected image and give some color to the background add android:padding="3dp" to grid_itemYogh
Does the onCreate for the GridView go in the SquareImageView.java file? or a new one?Phthisis
@Phthisis Thats intended for your Activity.Ardellaardelle
If it extends ImageView, how can you have a onCreate?Phthisis
@Phthisis 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.Ardellaardelle
NOTE: Setmax's newer answer seems simpler, for current Android versions. (No longer need to create CustomImageView class.)Boredom
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; }Gam
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 ?Blatman
@Blatman just use AppCompatImageView as suggested if you're using the support library.Ardellaardelle
D
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..

Dasyure answered 19/9, 2015 at 8:49 Comment(1)
With API 26.1 this got depricated. See linkDiaster

© 2022 - 2024 — McMap. All rights reserved.