Saturday, March 6, 2010

Handling Button clicks in a ListView Row


Lets say you have a spinner widget, like the one we created in our 'colours' example, and you wanted to put a button on each row of your spinner for your users to click (everyone loves clicking buttons, right?).

You might have a layout a bit like this simple one below:

<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">

 <TextView android:text="this is a row"
     android:id="@+id/tvViewRow"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content">
 </TextView>
 <Button android:text="Click me!"
     android:id="@+id/BtnToClick"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:onClick="myClickHandler">
 </Button>

</LinearLayout>

.. Containing just a textView and a Button View.

A listView with textual, non-clickable views in it responds to Click events via the OnItemClickListener event.

But once you put a button in the ListView, this event no longer fires.

So how do you capture the button's click event, and find what row in your ListView a clicked button is located in?

You may have noticed this android:onClick="myClickHandler"> in our layout above..
What this does is tie every click of every instance of that button, in every row of our ListView to one single handler, located in the code of our ListActivity class below:

    public void myClickHandler(View v) 
    {
          
        //reset all the listView items background colours 
        //before we set the clicked one..

        ListView lvItems = getListView();
        for (int i=0; i < lvItems.getChildCount(); i++) 
        {
            lvItems.getChildAt(i).setBackgroundColor(Color.BLUE);        
        }
        
        
        //get the row the clicked button is in
        LinearLayout vwParentRow = (LinearLayout)v.getParent();
         
        TextView child = (TextView)vwParentRow.getChildAt(0);
        Button btnChild = (Button)vwParentRow.getChildAt(1);
        btnChild.setText(child.getText());
        btnChild.setText("I've been clicked!");
        
        int c = Color.CYAN;
        
        vwParentRow.setBackgroundColor(c); 
        vwParentRow.refreshDrawableState();       
    }


In this case, the View v being passed in as a parameter is our button.
We get the parent of our button, being careful to cast it as a LinearLayout (have another look at the xml if you're not sure why), and then simply set the background colour of our Layout Row.

Don't forget to call .refreshDrawableState(); on your vwParentRow or it will never redraw and you won't see your nice colour change.

.. Tada!




Update: Hi everyone, thanks for all the interest, here's a link to the full project zipped.
Updated Update: Here's another link, and another, and another.

I've also updated the above myClickHandler to loop through the other items in the ListView and reset them to a default colour, in this case blue, to make it a bit more obvious what's going on. Hope that helps.

33 comments:

  1. Yes please provide source code. I'm having a problem with making a ListView with anything more than just a single TextView in it. I would like to make ListViews with multiple Views as you did.

    ReplyDelete
  2. I figured it out. First changed my ArrayAdapter constructor, then modified how you used getParent and getChildAt.

    Thanks for the reference.

    ReplyDelete
  3. How to I get the item data or position for the clicked view=button resp. its parent row?

    ReplyDelete
  4. But this ll chage the colour of all items when clicked. if i click in one item, colour gets chaged and then i click on another that also ll get changed so how to implement selector colour using this

    ReplyDelete
  5. Dude, Thanks. exactly what I was looking for.

    ReplyDelete
  6. If you add onItemClickListener on the ListView, can you click either the row or the button ? In ,y case after I added button, the row was no longer clickable.

    ReplyDelete
  7. Thanks a lot for the nice concept!!
    but the link that you provided doesn't work :-(
    google doc says that the document isn't available anymore.

    ReplyDelete
  8. Thanks Aman, that's weird, works for me.
    I'll upload it in a couple of other places for backup.
    Thanks for letting me know.

    ReplyDelete
  9. I tried this and get an error

    ERROR No resource identifier found for attribute 'onClick' in package 'android' menu_item.xml

    ReplyDelete
  10. this is the simplest approach i have got on net..worked like a charm!! thanks buddy..

    ReplyDelete
  11. how can i find out the row number from top which has been selected??

    ReplyDelete
  12. Thank u Very much ..looking for this topic..

    ReplyDelete
  13. In the listView button click event, i want to switch over to different activity in the activity group.how to do that.Please help.

    ReplyDelete
  14. Hi.Can somebody post link to source code, i have some problems with adapter.
    By the way,its better to post source code on google sites, and not on filetube or rapidshare. That way it wont be deleted after some time.
    Thanks.

    ReplyDelete
  15. Hi Alex,
    .. Thanks for visiting. The first link is to google docs and should work for you. Here you go.

    ReplyDelete
  16. the link seems to be dead or something cannot find the link, can you re check please ?

    ReplyDelete
  17. Hi guys,
    .. can you try again now?

    I've made it public in google docs, and I've checked it from another computer where I haven't signed into google and it works fine for me.

    It should take you to a page with a downloadable zip file of the project.

    ReplyDelete
  18. How can you capture the list item click AND the button click?

    ReplyDelete
  19. Hi Ben,
    Depending on who you ask, it may be either not really possible (as soon as you add a button you can no longer click on the row, and that's by design), or do-able with a bit of a hack.

    See here:
    http://groups.google.com/group/android-developers/browse_thread/thread/3d96af1530a7d62a/83deebc96c885b0b

    Hope that helps.

    ReplyDelete
  20. Hi there, if anyone could help me out please here. I am facing a problem and can not find the solution for this … My code is the following and i cant seem to find the errors but its just when the splash screen is exited towards the main page, just top button works, others don’t … but when tapped that button and return from there then all the buttons work. I want them all to work

    package net.xuting;

    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;

    public class xutingMenu extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    // button 1
    Button btn_live_tv = (Button) findViewById(R.id.btnLiveTv);
    btn_live_tv.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    startActivity(new Intent(“net.xuting.LIVETV”));

    //button2

    Button btn_Favorites = (Button) findViewById(R.id.btnFavorites);
    btn_Favorites.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    startActivity(new Intent(“net.xuting.FAVORITES”));

    }
    });
    //button 3
    Button btn_Featured = (Button) findViewById(R.id.btnFeatured);
    btn_Featured.setOnClickListener (new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    startActivity(new Intent(“net.xuting.FEATURED”));

    }
    });
    //button 4
    Button btn_Video_On_Demand = (Button) findViewById(R.id.btnVideoOnDemand);
    btn_Video_On_Demand.setOnClickListener (new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    startActivity(new Intent(“net.xuting.VIDEO_ON_DEMAND”));
    }
    });

    //button 5
    Button btn_Search = (Button) findViewById(R.id.btnSearch);
    btn_Search.setOnClickListener (new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    startActivity(new Intent(“net.xuting.SEARCH”));
    }
    });

    //button 6
    Button btn_Settings = (Button) findViewById(R.id.btnSettings);
    btn_Settings.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    // TODO Auto-generated method stub
    startActivity(new Intent(“net.xuting.SETTINGS”));
    }
    });
    }

    });
    }

    }

    ReplyDelete
  21. Hi Baiga,
    .. Have you tried debugging this and clicking the buttons to see what's going on?
    You could also try replacing your startActivity calls with a simple Toast notification and see if that works, you can find some instruction on creating Toasts here.
    If you're still not having any luck, please upload your project somewhere and I'd be happy to have a look at it for you.

    ReplyDelete
  22. Hi Glen,
    Thanks again for this nice post, but am having a problem with mine, My xml file also contains a RelativeLayout tag and within that i have the button tag in order for the buttons to be positions on the left, how do i get the button to respond to the click this time..

    Thanks.

    ReplyDelete
  23. Hi Serge, can you post your code so I can have a look?

    ReplyDelete
  24. hi
    It is a nice solution but can you please tell me how i implement it on listView
    I have class which extends Activity instead of ListActivity and in that activity I am using custom list, how do I implement custom listener for buttons.
    It is really urgent will appriciate your prompt reply.

    ReplyDelete
  25. Awesome... Was just searching for this only :D

    ReplyDelete
  26. Hello, could you tell me, how can i find out the row number of ListView using "MyClickHandler".

    ReplyDelete
    Replies
    1. @Invis

      public void myClickHandler(View v) {

      int position = getListView().getPositionForView(v);

      ...
      }

      Delete
  27. nice one ,but if button is in inner linear layout
    then how we get it.

    ReplyDelete
  28. Nice Code Dear thank.Nadir Pakistani

    ReplyDelete