Blog

Android: Creating a custom Adapter for GridView (ButtonAdapter)

Background
Adapters are great, it’s a fact. After you get over the initial learning curve you will realise you love them (almost as much as a six sided companion). This is my attempt at a casual explanation of how to create your own custom adapter, in this example we will create a ButtonAdapter similar to something you might see in a soundboard (yawn).

Example of a grid adapter in use

Example of a grid adapter in use

Advantages of an adapter

  • Dynamic – Can expand to any number of elements rather than statically coding each individual view.
  • Elegant – Makes your code petite and quite clear to understand once you get over the initial difficulty
  • Beautiful – Now you don’t have to control how many items there are in rows or columns, android will automatically fill up the screen in the best way possible. This also means you don’t need to redesign your application for horizontal and vertical orientations.

Creating a ButtonAdapter

So we’re going to dive straight into the deep end and create our own ButtonAdapter class that extends the BaseAdapter class. If you are doing this in eclipse you can write the first line and then it will offer to autocreate (implement) the missing methods for you (if you highlight the error). This code goes inside your Activity in your java file but not inside your oncreate method.

public class ButtonAdapter extends BaseAdapter {
 private Context mContext;

 // Gets the context so it can be used later
 public ButtonAdapter(Context c) {
  mContext = c;
 }

 // Total number of things contained within the adapter
 public int getCount() {
  return filenames.length;
 }

  // Require for structure, not really used in my code.
 public Object getItem(int position) {
  return null;
 }

 // Require for structure, not really used in my code. Can
 // be used to get the id of an item in the adapter for 
 // manual control. 
 public long getItemId(int position) {
  return position;
 }

 public View getView(int position, 
                           View convertView, ViewGroup parent) {
  Button btn;
  if (convertView == null) {  
   // if it's not recycled, initialize some attributes
   btn = new Button(mContext);
   btn.setLayoutParams(new GridView.LayoutParams(100, 55));
   btn.setPadding(8, 8, 8, 8);
   } 
  else {
   btn = (Button) convertView;
  }
  exus
  btn.setText(filesnames[position]); 
  // filenames is an array of strings
  btn.setTextColor(Color.WHITE);
  btn.setBackgroundResource(R.drawable.button);
  btn.setId(position);

  return btn;
 }
}

So the important methods are getCount and getView. getCount returns the number of objects (in our case buttons) that will be needed in this adapter. getView returns an object (again a button in our case) so that it can be used.

Both these functions reference an array that I have referered to as filenames this is a string array (String[]) which looks something like the following:

public String[] filesnames = { 
			"File 1", 
			"File 2",
			"Roflcopters"
			};

Creating an OnClickListener

You can add the following to your getView method to setup a new onclick listener for your buttons so that they can react to button presses.

  // Set the onclicklistener so that pressing the button fires an event
  // We will need to implement this onclicklistner.
  btn.setOnClickListener(new MyOnClickListener(position));

For this to work we need to implement our own OnClickListner which I have named MyOnClickListener (for lack of a better name) this is the same as a normal onclick listner except we pass an integer so that we can tell which button called our onClick method (you could get the id from the view passed, but this method is useful when expanding your program later on).


class MyOnClickListener implements OnClickListener
{
 private final int position;

 public MyOnClickListener(int position)
 {
  this.position = position;
 }

 public void onClick(View v)
 {
  // Preform a function based on the position
  someFunction(this.position)
 }
}

Implementing this adapter

Now implementing the adapater is very simple, add a few imports and load a grid view from an xml file. Then we simply set the gridview’s adapter to be a new ButtonAdapter and it will automatically do the rest for us.

// You will need the following imports

import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;

// In your oncreate (or where ever you want to create your gridview)
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ButtonAdapter(this));

And hopefully your all done, you could then go on to add a context menu(menu on long press) to your buttons. If you have any questions or want something explaining a bit better, just ask!

69 Comments

  1. Lucy

    Hi,

    I’m wondering if you could send me the completed soundboard project folder, with sava as ringtone function, i’ve tried following your tutorials on this but everytime i just end up in a complete mess.

    I’m trying to make a soundboard to entertain my friends kids while on a long journey flight.

    I would be ever so grateful

    Many thanks
    Lucy

  2. mat

    @Lucy No I’m afraid not, I want people to learn from what I have done rather than just copy and paste a packaged solution. I can give you help if you have specific problems though.

    There are plenty of soundboards and other things already out there that you could use to entertain your kids.

  3. Lucy

    @mat

    Ok Mat, i would love some help, shall i post my problem code here or send it via some other method to you.
    thanks
    lucy

  4. mat

    @mat Ask it as a question on stackoverflow.com (and post the link here as a comment so I can find it) as this is a good habbit to get into and offers the chance for other people to answer too.

  5. Lucy

    Hi Mat,

    Pleae help me with this, i would like to be able to set as ringtone, at the moment it works on the 1st sound (sound4) but not on the other one, also i would like to add more sounds.

    http://stackoverflow.com/questions/3675995/android-how-to-set-a-sound-as-ringtone-with-long-press-on-button

    thankyou
    lucy

  6. Michael

    hi Mat

    Could you please help me, i am a beginer in android programing. Im working on a homescreen widget for contact calling.
    1. I hava a problem when i just put the gridView to my widget.xml -> i get problem loading widget?? Do i have to implement something to make it work?
    2. I have a configuration form to select multiple contactacts . When a selection is made i want to dispplay them on my widget in a GridView. How do i send data from configuration activity to the widget gridView? With an adapter?

    Theank you for the replay. Im stuck here.

  7. mat

    @Michael Sorry your question isn’t very clear, try posting it on stackoverflow.com and add source code where applicable.

  8. Lucy

    Hi Mat,

    Have you had a look, i’m really stuck

  9. mat

    @Lucy I have, and I have replied. But you can’t always rely on me for help that’s why I suggested stackoverflow as other people can jump in too.

  10. Lucy

    I really dont get where i should add the code you mention, please could you reply again with what the SoundBoardTest.java code and SoundManager.java code should actually be, (with 10 sounds, sound1, sound2 etcc..)

    i promise i will leave you alone after this

    Pleeeeaasssee…..
    🙂

    Lucy

  11. mat

    @Lucy Please discuss this on stackoverflow.

  12. Lucy

    I have posted back on stackoverflow

  13. Lucy

    Hi,

    Will you help me ?

    Lucy

  14. mat

    @Lucy Yes, please be patient. I will not answer any faster if you keep pestering me.

  15. m3n0R

    Hi Mat!
    Do you remember I asked you about your scoreboard for my Android application?

    Well i have developed : http://ateneatech.com/blog/damos-la-bienvenida-publica-time-bird

    With togglebuttons and tablelayout, because is impossible to have a togglebutton (dynamic number of..) with a gridview, right?

    Now, i’m working on holding button action, named “context menu” which is easy to do :

    final ToggleButton tb0 = new ToggleButton(this);
    	    		
    	    		 registerForContextMenu(tb0);
    

    and…

    @Override 
    	 public void onCreateContextMenu(ContextMenu menu, View v,  ContextMenuInfo menuInfo) {  
    		 //if (v.getId()==R.id.list) {  
    			 AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;  
    			 //menu.setHeaderTitle(phones[info.position]);  
    			 menu.add(0, ITEM_EDIT, 0, "Edit");  
    			 menu.add(0, ITEM_DELETE, 0,  "Delete");
    	 	//}  
    	 } 
    
    	@Override
        public boolean onContextItemSelected(MenuItem item) {  
        	//TextView text = (TextView)findViewById(R.id.footer); 
        	switch (item.getItemId()) {  
        		case ITEM_EDIT:    
        			//text.setText("Edit selected");    
        			return true;  
        		case ITEM_DELETE:    
        			//text.setText("Delete selected");  
        			return true;  
        		default:    
        			return super.onContextItemSelected(item);  
        	}
        }
    

    The problem is… because this is not a view.. i can’t obtain the button’s id. Do you know if it is possible?

    Thanks a lot guy!

  16. mat

    @m3n0R You could quite easily create a gridview of toggle buttons in the same way as I have demonstrated for regular buttons.

    In my examples I pass v.getId() into the menu that is created and then use the title in the switch statement for the selected like so:

    // In onCreateContextMenu
    menu.add(0, v.getId(), 0, "Save as Ringtone");
    
    // onContextItemSelected
    if(item.getTitle()=="Save as Ringtone") { saveas(item.getItemId() } 
    

    I’m sure there is a better way of doing it, but it works! Hope that helps!

  17. m3n0R

    Thank you so much. You solved my problem.
    I want yo ask you something…

    Do you know if it’s possible to do simple button with togglebutton functionallity (switched on and switched off), withouth using boolean vars for every button(to know if it’s on/off)?
    I would really interested in, because if I change togglebutton image, the position of button text is impossible to change it. I tried to do that, changing text position, but in togglebutton is fixed!! (or i think so)

    Thank you again!

  18. mat

    @m3n0R Why not create an array of booleans for use with the adapter. Something like:

    // Declaration of boolean array
    Boolean toggles[];
    // Allocation of boolean array
    toggles = new Boolean{true,true,true....}
    // In the adapter somewhere you'll want to set / get the value based on postions
    // Setting the value
    toggles[pos] = true;
    // Getting the value
    return toggles[pos]
    

    Hope that answers your question.

  19. m3n0R

    Yeah!!! thank you too much!

  20. Blundell

    Matt, I’ve got gridview under wraps and I’m loving these adapter’s trouble is I want my getView() method to return a more complicated layout than just a button or imageview, I want to return a LinearLayout with an image view and text inside it, is this possible and if not what would you recommend as an alternative?

    cheers

  21. mat

    Yes, that is easily possible (have done this before) just create a linear layout programatically and add whatever views you want to it and then just return the linear layout. You can even just inflate a layout from an xml file and return that.

  22. malc cooke

    I’ve been doing similar thing with a gridview populated by buttons and using an adapter derived from the base adapter. The buttons get clicked and basically the adapter notifyDatasetChanged method is called to notify the changed positions within the gridview. This code worked fine in Android 1.6 – 2.1 but doesn’t work with 2.2. I was wondering if you had any experience of this problem at all.

    With 2.2, the behaviour is a bit odd – the first two button clicks work as expected ie the onClick method is called and notifyDatasetChanged is triggrered, but on the third click it seems to hang within the notifyDatasetChanged method call.

    If it helps I have some v simplified code I could put up here that replicates the problem ie it works fine with 1.6 – 2.1 but not 2.2. Any suggestions ? Any thoughts where to investigate further?

    Thanks in advance.

    Malc

  23. mat

    @malc Post your question along with some sample code on stackoverflow.com and you will probably get an answer. Remember to post back here with a link to your question so I can have a look also!

  24. malc cooke

    @mat – thanks for response – code was posted up on stackoverflow 2 days ago – no response as of yet. Here is link to the question:

    http://stackoverflow.com/questions/3913970/notifydatasetchanged-not-working-in-android-2-2-but-works-in-1-6-fine-and-i-beli

    I am quite prepared to believe that it is something I am doing wrong – but in that case v puzzled why it works in versions pre 2.2

  25. Ken

    Great tutorial.

    I have one question.
    How do I get a button onclick to start a new activity? I have been trying for a couple of days and just can’t get it to work. Your assistance on this would be much appreciated.

  26. orech

    Hello,
    thanks for this great tutorial.

    But I’ve one Qs 🙂

    I’ve set my own colors for buttons, but buttons are gray with black text 🙁
    This is my code:

    btn.setTextColor(Color.YELLOW);  
    btn.setBackgroundColor(Color.GREEN);
    

    Thanks for help in advance

  27. orech

    Sry, works now…maybe an emulator bug 🙂

  28. Sean Fair

    Thanks for the great learning tool. I am wondering what the .xml looks like for this. I need a GridView and a Button, but do I need to have them in any special format. Do I put the button inside the GridView? I am not really sure, sorry about that I am new to Android development. I am an old VB developer.

    Thanks,

    Sean

  29. Bina

    Hello,
    i have images in gridview how to set different condition on different imageview on OnClickListener.please explain i am new in android.

  30. Cesar

    Hi Matt, I don’t know if you remember me. I’ve asked you questions in your blog about android programming before.

    I ask you this question because you’re expert in make tablelayouts or grids and buttons with contextmenu, because of your soundboard applications. I would like to know what’s wrong or what can I do to solve the problem I’ve been trying to solve for two weeks and I haven’t a solution yet.

    Here is the post in stackoverflow:http://stackoverflow.com/questions/5529201/onitemcontextmenu-and-button-created-programatically-problem

    Could you help me please? I would be so grateful… Thank you so much,

    m3n0R

  31. MacDegger

    Thanks for the info; great and clear tut!

  32. Aurora Borealis
    class MyOnClickListener extends Activity implements OnClickListener  
        {  
         private final int position;  
          
         public MyOnClickListener(int position)  
         {  
          this.position = position;  
         }  
          
         public void onClick(View v)  
         {  
          Intent intent=new Intent(MyOnClickListener.this,another.class);
    		startActivity(intent);
         }  
        }  
    

    Why thats not working?.please help me

  33. mat

    Your intent should be given ActivityName.this not MyOnClickListener.this

  34. asha

    @mat please help

    I have a doubt in my progarm.My project is to show three option buttons in Main page and once i clicked on the button must go to another page.My code is

    public class GridViewDemo extends Activity
     {
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate( savedInstanceState);
    setContentView(R.layout.main);
    
    GridView g=(GridView) findViewById(R.id.grid);
    g.setAdapter(new ButtonAdapter(this));
    
    
    }
    
    public class ButtonAdapter extends BaseAdapter {
        public ButtonAdapter(Context c) {
            mContext = c;
        }
        public int getCount() {
    		
    		return items.length;
    	}
    	
    	public Object getItem(int position) {
    		return position;
    		
    	}
    	
    	public long getItemId(int position) {
    		
    		return position;
    	}
        
    		public View getView(int position, View convertView,
    				ViewGroup parent) {
    			
    			Button btn;
                if (convertView == null) {
                    btn = new Button(mContext);
                    btn.setLayoutParams(new GridView.LayoutParams(2000, 55));
                   
                    btn.setPadding(0, 0,0,0);
                } else {
                    btn = (Button) convertView;
                }
    				btn.setText(items[position]);
    				btn.setTextColor(Color.WHITE);
    				btn.setId(position); 
    				
    				if(position==0){
    			btn.setOnClickListener(new OnItemClickListener() {
    			            public void onItemClick(AdapterView parent, View v, int position, long id){
    			            	Intent intent = new Intent(v.getContext(),Grid1.class);
    			        		  Bundle bundle = new Bundle();
    			        		 
    			        		  intent.putExtras(bundle);
    			        		  startActivityForResult(intent, 0); 
    			            }
    				});
    				return btn;
    		}
    		    
    		   }  
    		private Context mContext;
    		private String [] items ={"1) To display images","2) From SD cards","3) From Internet"};
    		 
    		
    }	
    		}

    I have created a class Grid1.java to display images from resources.
    But this code is not working and shows an error
    “The method setOnClickListener(View.OnClickListener) in the type View is not applicable for the arguments (new
    AdapterView.OnItemClickListener(){})” please help me to resolve.

  35. Jon

    I’m having problems playing a sound file.

    The buttons generate perfectly but when I call a function from the onClick I get a FC

    05-02 00:26:04.409: ERROR/AndroidRuntime(1364): at android.content.ContextWrapper.getResources(ContextWrapper.java:80)

    The way I’ve tried doing this so far is like

    	@Override
    	public void onClick(View v) {
    		// TODO Auto-generated method stub
    		function(this.position); 
    	}
    	
    	 private void function(int buttonposition) {
    		if (buttonposition == 1) {
    			Toast.makeText(this, "test", Toast.LENGTH_SHORT).show();
    		} else if {
    			Toast.makeText(this, "test2", Toast.LENGTH_SHORT).show();
    		} else {
    			Toast.makeText(this, "test3", Toast.LENGTH_SHORT).show();
    		}
    

    But this doesn’t appear to change anything- I get the same error. The same thing happens if I just hardcode something into the onClick but I’m assuming thats due to no attempt at identifying which button was clicked..

    Do have any insight into what I’m doing wrong? Thanks man

    (P.S My goal isn’t for a toast message to come up but I’m sure I could work it out if I could get the toast messages to work)

  36. Jon

    * } else if (buttonposition == 2) {

  37. Jon

    Also posted on stackoverflow

    Any help will be much appreciated

    http://stackoverflow.com/questions/5891736/problem-identifying-generated-gridview-button

  38. mat

    If you contact me via the contact form on this website you can send me your project in a zip and I can have a quick look at it for you.

  39. vinit

    Hi mat,

    I want to select multiple images in grid view but i am not getting the way how to do it? actually in my application i want to download multiple images selected by the user in gridview(something like checkbox with image)not a list view . please help me regarding it.
    thanks in advance

  40. mat

    @vinit Well I have an idea of how I’d start this. For the gridview rather than just returning a button as the view, return a LinearLayout which contains your checkbox and an imageview or whatever. Then you can make a function that iterates through all items in your listview to see if they are checked or not and performs a specific operation on the ones that are selected. I don’t have enough time currently to provide you with coding help, but you could post on stackoverflow or just google to see if someone has done anything similar already rather than reinventing the wheel.

  41. John

    Hi mat, first i want to say thanks you for this post, its awesome and you’re right its better to make a grid dinamically. I have one problem, i want that when focusing over an element a TextView displays information about that specific element.

    I’ve tried using findViewById inside the class that extends BaseAdapter, but it tells me that findViewById is undefined.
    I wonder if its posible to get an element id within the class that’s setting the grid and then to set my TextView.

    Thank you so much

  42. John

    Hi again mat, i hope you can help me with a problem that i have, i followed your tutorial and i have my gridView working fine, the problem is that i have also a TextView element under the gridView and i want that when pressing certain button, the TextView shows which button has been pressed. I’m getting a NullPointerException from my getView method, i hope you can help me, my question is on stackoverflow:
    http://stackoverflow.com/questions/7126029/work-with-dynamic-elements-from-a-custom-adapter

    You can delete my other post.
    Thanks in advance

  43. Nick

    Great write up! My last sound board I individually coded every onclick/xml button/longclick, haha. This made it infinitely easier, Thanks!

  44. vr

    Mat great thing that you have done!
    In the reply to Aurora Borealis you told her “Your intent should be given ActivityName.this not MyOnClickListener.this”.
    Sorry but I didn’t understood beacause we can’t have ActivityName on this file, at leat I think we can’t. Could you expain how could we do it? Thank you

  45. vr

    Thanks, but I’ve resolved it… 🙂

  46. Android: Gridview force closes

    […] am following a good tutorial on using a gridview. I have been unable to get the code to work however as ever time I compile and run the app force […]

  47. Nick

    Hi Mat, I used this exact method to display the buttons in my soundboard app. When I don’t include a drawable background the buttons are the normal google gray and fill my screen fine both in my emulator and actual hardware. However, when I reference a drawable background that I created the buttons do not stretch correctly. On my phone they look small and do not fill the screen. In the emulator they look big. The resolution on my emulator is low and my phone is high. Do you have a quick answer to my problem or do you suggest I post my code on stackoverflow? I have all the same coding as your example so I don’t know what is wrong other than the graphic I created maybe? Thanks!

  48. Gabriel

    Hi mat, can i add a ListView inside a block of the GridView? or theres a better way to do that without much problem? thanks for any help given

  49. John

    I am confused as why you used:
    filesnames and filenames
    there is an extra “s” in the first one after file.

    “WHY?” I cry in despair.

    Thanks.

  50. D

    I am trying to make a grid of toggle buttons and I am trying to analyze your approach so that I might formulate my own. However, there is a line (38) in the first code segment you posted that is the work “exus”. I was unable to find that term/keyword/etc. Could you please help me?
    Thanks!

Leave a Comment

Your email address will not be published. Required fields are marked *