In the screenshot above you can see a ListView with custom built items. Each list item consists of
- TextView for the title
- TextView for the content
- RelativeLayout (clickable) consisting of
- TextView: "For more information click here"
- ImageView for the arrow icon
1) Build custom adapter that extends BaseAdapter and implement all the required inherited abstract methods from the parent class:
2) Implement View getView(int position, View convertView, ViewGroup parent):
This is where you create and manipulate the view that is used for a list item. I highly recommend that you take advantage of a view holder as it is described in this API example to improve speed and efficiency.
The crucial part is now that you, after you have inflated the list item's main layout
you also need to get hold of the sub-layout (bottom part). That's the part you want to be clickable.
3) Create an onClickListener for the bottom layout and associate the data (in this case a web URL) with the current view:
While for a OnItemClickListener, which is associated to each item of a ListView, we always know which item of the list has been selected through the int position paremeter. We don't have this information for our OnClickListener that is used in the bottom layout. So how do we get the neccessary information that is associated to the current list item?
The trick is to use View.setTag() and View.getTag(). The Android documentation states: “Sets the tag associated with this view. A tag can be used to mark a view in its hierarchy and does not have to be unique within the hierarchy. Tags can also be used to store data within a view without resorting to another data structure.”
In this example we use setTag() to store an URL with each sub-layout and getTag() in the onClickListener to extract that URL whenever a sub-layout was clicked. Then we create an Intent for our WebView, pass the URL to it and start a new Activity. And Voilà! You should have a web browser now displaying the website associated to the item you clicked before.
However, there's one little detail that keeps annoying me. Even though I now can click/tap the bar at the bottom via the touch screen interface I'm not able to select it via the trackball. It always selects the whole list item (see screenshot below) and from there goes directly to the next list item ignoring the bottom bar, even though I set
.setFocusable(true)
and setClickable(true)
for the bottom bar's RelativeLayout in getView()
.If anyone knows a solution to this please let me know in the comments or respond to my question asked on stackoverflow.
I've just launched a new side project of mine:
Bugfender - A modern remote logger tailor-made for mobile development
It's currently free and you can sign up here: https://app.bugfender.com/signup
Any kind of feedback is more than appreciated :-)
Hi Stefan
ReplyDeleteit's a very helpful example indeed but could you please elaborate as i am stuck with on click of a button in a list.
thanks
Hey snigdha. Sure. Just let me know what your problem is and I'll try to help.
ReplyDeleteStefan
ReplyDeleteThanks for the tutorial. One question, how would you go about removing an item from the list when you click a button in that item? I'm currently using a custom cursor adapter and in my onclicklistener I remove the item form the database and then do a cursor requery, but all the items in my list disappear. If I go away and come back it correctly shows the remaining items. It's driving me crazy. Do you have any ideas? Thanks again.
Hey Mike,
ReplyDeleteSorry, I've not worked with cursors/databases on Android, yet. So I'm not really able to help you at the moment. I'd suggest you ask your problem on http://stackoverflow.com/ - there you should usually get a response very quickly.
@Mike
ReplyDeleteextend ArrayAdapter instead of BaseAdapter.
You can simply call remove(item) on the adapter.
Don't forget to call notifyDataSetChanged() on the adapter as well. This will update the ui.
Hi Stefan,
ReplyDeleteThanks for this example - it saved me many hours.
I tried to optimize it a bit more and it seems to be working. Basic idea is to have only one View.OnClickListener per clickable element in all rows. So instead of calling new View.OnClickListener() on every clickable in every row, I put these listeners as members of the adapter class. Since they operate on tags, they know which widget triggered them.
public class MyAdapter extends BaseAdapter {
private View.OnClickListener mMoreInfoClickListener = new View.OnClickListener() {
//(...)
};
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//(...)
holder.layout_bottom.setOnClickListener(mMoreInfoClickListener);
}
I'm pretty new to Java and Android, and I'm not sure if this is valid approach, but as I said it seems to be working nicely.
Hey ajiree,
ReplyDeleteThanks for sharing your improvement. Using just one Listener definitely makes sense.
Hi,
ReplyDeleteThx for this tutorial.
But, I have some questions ??
When I click on the button, the OnClick in the Adapter works, but where I do the link with my Activity where is my List?
I Have a list with one ImageView and TextView. When user click on textview , it open another Activity. When user click on Button, I would like to open a dialog.
Thanks Stefan. This helped.
ReplyDeletePunit
Hi Stefan!
ReplyDeleteI've tried your solution and its great, but i have a problem.
After adding the .isClickable to the Layout my button is clickable but not the whole listview anymore.
I need click on the ListView (expands more fields) and click on the button inside the ListView.
Like the Gmail app.
Do you have a solution?
Hi Stefan,
ReplyDeletethank you for this helpful tutorial .
I want to make my list look like yours, I would like alternate the backround color of raws . would you please help me and indicate wich method can i use . or a source code if you have .
thank you
Just use a simple "if (rowNumber % 2) { set color 1 } else { set color 2}
ReplyDelete@chrisonline
ReplyDeletetry onitemclick of listview
your whole listitem is then clickable
@chrisonline
ReplyDeletetry onitemclick of listview
like this
yourList..setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView parent, View view, int position, long id) {
//to do when item is clicked
}
};
your whole listitem is then clickable
Thanks for helping clear this up! Your tutorial was great.
ReplyDeleteThe API Example link in the article appears to be broken, btw. The List14 example has moved to
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List14.html
Thanks. Link is fixed :-)
ReplyDeleteAnswer to focus from stack overflow:
ReplyDeleteProbably you've found how to do it, but you can call ListView.setItemsCanFocus(true), and your buttons will catch focus...
Hello SteFan,
ReplyDeleteI m confused on one things, i have a situation in which i have a list view with imageview (Which i want to make clickable), so if user clicks on any item, it will do some process and if user click on imageview, then a google map will open which will show position marked using latitude and longitude. i have a array of latitude and longitude. Now how i need to settag, so i can get a particular set of latitude and longitude for a google map, when user click on image view ?
Hi,
ReplyDeleteI am pretty new to android and hence dont know much about customized list views and adapters. I would be thankful if you could provide me with the complete code and a bit of explanation regarding it.
Thnx..
Did you manage to solve your focus problem?
ReplyDeleteThanks.
Posted a follow-up on SO on how to get the adapter position for a clicked View in a ListView row:
ReplyDeletehttp://stackoverflow.com/questions/1709166/android-listview-elements-with-multiple-clickable-buttons/5320540#5320540
@GKB: Thanks :-)
ReplyDeleteThank you for the great blog in here...but could you please help me on the same lines...I have posted my question in SO..kindly have a look
ReplyDeletehttp://stackoverflow.com/questions/6535558/call-notifyondatasetchanged-from-another-adapter
hi there, i also newbie here..
ReplyDeletei really hope that you can give more code for this example..please
i had my hard time trying to make this works..
I would be grateful if you could do a write-up for custom ListViews!
ReplyDeleteThanks
Hello thnx for tutorial
ReplyDeletebut i have a little problem which is related to ListView.
I have a list view to which i bind a user information list having 2 TextViews and 1 button.
On click of the button i want to get whole object of that user and pass it to next page.
I have written everything i could but my button click is not working.
Can u please help me in it?
si eres de barcelona, debes hablar español no? en todo caso te queria preguntar si sabes acerca de personalizar tableLayouts en Android, porque tengo una duda hacerca de incluir ListViews dentro de una celda en la tabla, no se como se podria o que tanta versatilidad tiene, asimismo con la lista dentro de un GridView, estoy tratando de pensar como hacerlo y honestamente no se me ocurre nada, saludos y que buen trabajo
ReplyDeleteGreat post.!
ReplyDeletebut I wonder why did you use TextView and ImageView inside RelativeLayout instead of using single TextView with drawable at right property.
However the post was very helpful for me.
Hi stefan,
ReplyDeleteI am beginner in android. I created a listview with elements which i fetched from online. I want to add a button once i select a listelement. For eg: if i select the third row, i want to add a button on that row. Can you please help me with the code. I have my code here:
package com.pxr.tutorial.xmltest;
import java.util.ArrayList;
import java.util.HashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
public class Main extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listplaceholder);
ArrayList> mylist = new ArrayList>();
String xml = XMLfunctions.getXML();
Document doc = XMLfunctions.XMLfromString(xml);
int numResults = XMLfunctions.numResults(doc);
if((numResults <= 0)){
Toast.makeText(Main.this, "Geen resultaten gevonden", Toast.LENGTH_LONG).show();
finish();
}
NodeList nodes = doc.getElementsByTagName("product");
for (int i = 0; i < nodes.getLength(); i++) {
HashMap map = new HashMap();
Element e = (Element)nodes.item(i);
map.put("id", XMLfunctions.getValue(e, "productId"));
map.put("name", "Name:" + XMLfunctions.getValue(e, "name"));
map.put("id", "ProductId: " + XMLfunctions.getValue(e, "productId"));
map.put("type", "Type: " + XMLfunctions.getValue(e, "type"));
map.put("rprice", "RegularPrice: " + XMLfunctions.getValue(e, "regularPrice"));
map.put("Score", "Sale Price: " + XMLfunctions.getValue(e, "salePrice"));
mylist.add(map);
}
ListAdapter adapter = new SimpleAdapter(this, mylist , R.layout.main,
new String[] { "name", "id","type","rprice","Score"},
new int[] { R.id.item_title, R.id.item_subtitle,R.id.item_subtitle1,R.id.item_subtitle8,R.id.item_subtitle9 });
setListAdapter(adapter);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
//lv.setOnItemClickListener(new OnItemClickListener() {
//public void onItemClick(AdapterView parent, View view, int position, long id) {
// @SuppressWarnings("unchecked")
// HashMap o = (HashMap) lv.getItemAtPosition(position);
// Toast.makeText(Main.this, "ID '" + o.get("id") + "' was clicked.", Toast.LENGTH_LONG).show();
//}
//});
lv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Button button1=new Button(this);
button1.setText("Click");
this.setContentview(button1);
}
});
}
}
Thanks in advance,
Gowtham.