Monday, August 1, 2011

Tutorial: Custom gallery, circular, and selected item enlarged

In my last project I had to provide user with a way to pick an item from a selection. Every item is represented by an image, so I chose to use the android gallery.
But as you probably know the default android gallery view is not very encouraging.
So I surfed the web for interesting ideas, knowing I would find a nice working example of some custom gallery.
I was surprised though to find out that 90% of the posts merely explain the same sample from the android dev guide.
So after a certain time of  hard work and thank to some ideas I got from StackOverFlow, I finally got a nicely working result. It is nothing fancy but suits the purpose perfectly.
Here is a small tutorial that explains how it is done.

Before you read it, you can look at the images below taken on HTC Desire or download a sample apk to see how it will look on your device.



This video was taken from android emulator on my pc:




Lets get down to business. The first thing we have to do is to declare a class that extends Gallery:


public class YappsCircleGallery extends Gallery
{
...
}

Then we will have to add some constructors:

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

Now we want to create an adapter for our Gallery:

public class ImageAdapterCircleGallery extends BaseAdapter 

It will have 2 members: a Context so we have the context we're working on, and an Int array with our drawable ids.

        private Context mContext;


private Integer[] mImageIds = {
           R.drawable.a,
           R.drawable.b,
           R.drawable.c,
           R.drawable.d,
           R.drawable.e
   };

In the constructor we save our context:

public ImageAdapterCircleGallery(Context c) {
       mContext = c;    
}

The easiest way to create a circular gallery is to make the adapter believe we have an endless (almost) number of items:

public int getCount() { 
       return Integer.MAX_VALUE; 
}

The trick is that we will tell the adapter our items go in a sequence of: 1,2,3.....n,1,2,3....n,1,2,3......n............... Then, at application start, we will present the item which is positioned in the middel of this sequence first, so the user will get the feeling of a circular gallery.
To calculate the image/view position out of the position we receive from the adapter, we will use the next function:

int getPosition(int position)
{
if (position >= mImageIds.length) { 
            position = position % mImageIds.length; 
        } 
        return position; 
}

And now we can override the rest of the adapter functions:

    public Object getItem(int position) { 
        return getPosition(position); 
    } 

    public long getItemId(int position) { 
        return getPosition(position); 
    } 

    public View getView(int position, View convertView, ViewGroup parent) 
    {
        ImageView i = new ImageView(mContext); 
        position= getPosition(position); 
        i.setImageResource(mImageIds[position]); 
        i.setLayoutParams(new Gallery.LayoutParams(200, 200)); 
        i.setScaleType(ImageView.ScaleType.FIT_XY); 
        return i; 
    } 

* The size I am using in the setLayoutParams will be the size of a regular (background) item, not the selected one.
Now we're finished with the adapter and can go back to extending the Gallery
We want to add the adapter and some new behavior to the Gallery.
So we will create a new function named initiateAdapter(Context) and call it from each of the 3 constructor we created previously.

private void initiateAdapter(Context context)
{
setAdapter(new ImageAdapterCircleGallery(context));
       
 //To select the xSelected one -> 0 is the first.
        int xSelected=0;
        //To make the view go to the middle of our 'endless' array
        setSelection(Integer.MAX_VALUE/2+(Integer.MAX_VALUE/2)%5-1+xSelected);
}

At this point a circular Gallery is ready. The only aspect left is to make the selected image larger then the background ones.
We will need to save the regular view before enlarging the item so we can revert it to it's previous state. That's why we're are adding a view member to our extended class:

View lastSelectedView=null;

and then we are adding a setOnItemSelectedListener to the initiateAdapter function:

this.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) 
{
if(lastSelectedView!=null)
lastSelectedView.setLayoutParams(new Gallery.LayoutParams(200, 200));
arg1.setLayoutParams(new Gallery.LayoutParams(250, 250));
lastSelectedView=arg1;
}
@Override
public void onNothingSelected(AdapterView<?> arg0)
{
}
});

Now all we have to do is to add our New Custom Gallery to the project.
In the Layout Xml we add:
<view  class="il.yapps.views.ciclegallery.YappsCircleGallery" 
android:layout_width="fill_parent" android:id="@+id/gallery" 
android:layout_height="250px" android:layout_margin="5dip"/>

* Once again the number 250 in android:layout_height="250px"  should match the same number 250 in the setLayoutParams method in setOnItemSelectedListener - this is the size of the selected image.

At last, we add it to the code:
       YappsCircleGallery yappsGallery = (YappsCircleGallery) findViewById(R.id.gallery);


If you liked this tutorial or have a suggestion to make please leave a comment :)

30 comments:

  1. super post, Evgeni.
    Could you figure out how to avoid the center-locking behavior of the Gallery?

    ReplyDelete
  2. Babu tnx,
    I didn't tried to avoid it, All I wanted to do, is what you see here: circular enlarged selected, the center locking is actually good for my project.

    ReplyDelete
  3. Very Nice Blog. It happened lot. Thanks Very Much

    ReplyDelete
  4. Very useful post, thanks for sharing.
    I experience my application crashing when changing the orientation of the the phone, from portrait to landscape. Does it happens to you as well?

    ReplyDelete
  5. Elena, it never crashed for me (but i block it to portrait),
    Does the crash happen in my example code?

    ReplyDelete
  6. ekich numberr..!!

    ReplyDelete
  7. Hi, To fix the orientation crash yous just have to add a parameter in Manifest File like that:



    tnx for this tuto,

    ReplyDelete
  8. I think I love you.

    ReplyDelete
  9. really done great job

    ReplyDelete
  10. Wow, thanks mate easy to understand and perfect for what i wanna do. U helped me a lot with this tutorial.

    ReplyDelete
  11. thanks alot Yapp You free me from a big burden from my head...
    Its really too good...

    ReplyDelete
  12. thank you very much, it was very helpful for me

    ReplyDelete
  13. thank you for this tutorial. But, I can't get access to the source code; the link dont't work

    ReplyDelete
  14. thank you very much,it is greate.you can create event, when you touch on item in gallery

    ReplyDelete
  15. Nice tutorials, want select a Image and display on image view:

    public Object getItem(int position) {
    return getPosition(position);
    }


    This method is not returning me correct position for display on adapter item listner, please help me how to fix this.

    Thanks,
    Naveen

    ReplyDelete
  16. hi,

    I want Image + Text View both in gallery interface

    public View getView(int position, View convertView, ViewGroup parent)
    {
    ImageView i = new ImageView(mContext);
    position= getPosition(position);
    i.setImageResource(mImageIds[position]);
    i.setLayoutParams(new Gallery.LayoutParams(200, 200));
    i.setScaleType(ImageView.ScaleType.FIT_XY);
    return i;
    }

    want customize this code using LayoutInflater.

    Facing issue, Please help me what to do for that

    ReplyDelete
  17. Great library,
    working till 4.1 and crashing in 4.2.2

    ReplyDelete
  18. how to use it for SDcard image in particular folder ...And how to set on click listner on it.

    ReplyDelete
  19. Amazing buddy......gr8 job...thanks a lot!!

    ReplyDelete
  20. Thanks in adv. kindly help me how to get a selected position index.

    ReplyDelete
  21. Chert poberi! Awesome! Tvou mat'! Realy great tuto!

    ReplyDelete
  22. Hi,

    I want to make it like this link, can you please give me an idea to do it with your current code.

    http://stackoverflow.com/questions/15897922/android-how-to-create-default-gallery-to-circular-gallery


    Best regards,

    ReplyDelete