Guide: Android – MultiClickable ListView:

Well today I want to talk about implementation of a multiclickable list.
And what do I mean by that? Well what I mean is that every list item has
more then one location where you can press it following by different operation.

So lets for example take a row where you want to place a CheckBox, ImageView
and a TextView while all of them are clickable. Meaning that you can click the
row it self for going to another Actvity for more details on the row, check its
CheckBox or press the ImageView to perform another operation.

So what you should do is:

1. First create an XML layout file for your ListView row:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
    <CheckBox 
        android:id="@+id/cbCheckListItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_marginLeft="10dp" />
    <TextView
        android:id="@+id/tvItemTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="item string" />
    <ImageView
        android:id="@+id/iStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:contentDescription="@drawable/ic_launcher"
        android:src="@drawable/ic_launcher" /> 
</LinearLayout>

2. Second in your java code define a ViewHolder, a ViewHolder
is designed to hold the row views and that way operating more quickly.

static class ViewHolder 
{
    TextView title;
    CheckBox checked;
    ImageView changeRowStatus;
}

3. Now we have to define CustomArrayAdapter, using the array adapter
we can define precisely what is the desired output for each row based on the content of this
row or it’s position. We can do so by overriding the getView method:

private class CustomArrayAdapter extends ArrayAdapter<RowData>
{	
    private ArrayList<RowData> list;
    
    //this custom adapter receives an ArrayList of RowData objects.
    //RowData is my class that represents the data for a single row and could be anything.
    public CustomArrayAdapter(Context context, int textViewResourceId, ArrayList<RowData> rowDataList) 
    {
        //populate the local list with data.
        super(context, textViewResourceId, rowDataList);
        this.list = new ArrayList<RowData>();
        this.list.addAll(rowDataList);
    }
		
    public View getView(final int position, View convertView, ViewGroup parent)
    {
        //creating the ViewHolder we defined earlier.
        ViewHolder holder = new ViewHolder();) 
        
        //creating LayoutInflator for inflating the row layout.
        LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        //inflating the row layout we defined earlier.
        convertView = inflator.inflate(R.layout.row_item_layout, null);
        
        //setting the views into the ViewHolder.
        holder.title = (TextView) convertView.findViewById(R.id.tvItemTitle);
        holder.changeRowStatus = (ImageView) convertView.findViewById(R.id.iStatus);
        holder.changeRowStatus.setTag(position);
 
        //define an onClickListener for the ImageView.
        holder.changeRowStatus.setOnClickListener(new OnClickListener() 
        {			
            @Override
            public void onClick(View v) 
            {
                Toast.makeText(activity, "Image from row " + position + " was pressed", Toast.LENGTH_LONG).show();
            }
        });
        holder.checked = (CheckBox) convertView.findViewById(R.id.cbCheckListItem);
        holder.checked.setTag(position);

        //define an onClickListener for the CheckBox.
        holder.checked.setOnClickListener(new OnClickListener() 
        {		
            @Override
            public void onClick(View v)
            {
                //assign check-box state to the corresponding object in list.    
                CheckBox checkbox = (CheckBox) v;
                rowDataList.get(position).setChecked(checkbox.isChecked());
                Toast.makeText(activity, "CheckBox from row " + position + " was checked", Toast.LENGTH_LONG).show();	 
            }
        });

        //setting data into the the ViewHolder.
        holder.title.setText(rowDataList.get(position).getName());
        holder.checked.setChecked(rowDataList.get(position).isChecked());

        //return the row view.
        return convertView;
    }
}

4. Now you need to set this adapter, as the adapter of your ListView.
this ListView can be created in java or using an XML file, in this case I’m using a list that was
defined in the XML file using the “list” id:

public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layout); 
    ListView list = (ListView)findViewById(R.id.list);
    CustomArrayAdapter dataAdapter = new CustomArrayAdapter(this, R.id.tvItemTitle, rowDataList);
    list.setAdapter(dataAdapter);
}

5. Finally if we want to be able to press the row it self and not only a certain view in it
we should assign an onItemClickListener to the ListView:

list.setOnItemClickListener(new OnItemClickListener() 
{
    public void onItemClick(AdapterView<?> parent, View view,int position, long id) 
    {
        Toast.makeText(activity, "row " + position + " was pressed", Toast.LENGTH_LONG).show();
    }
});

8 Responses to this entry

  • Chris Says:

    Can you provide your RowData class? Im not understanding how you can set the text of each row differently when you are not passing position to getRow()… Would RowData not have an array of all titles?

    Posted on September 7th, 2013 at 9:27 am Reply | Quote
  • Emil Adjiev Says:

    @Chris hi, Sorry I can’t provide it because it part of a work project I have worked on. For your question the RowData class should represnt all the data for a SINGLE row in the ListView. So no RowData will not have an array of all the titles but only the title for the specific raw the RowData class come to represent.

    Posted on September 22nd, 2013 at 12:48 pm Reply | Quote
  • Fosco Says:

    Hi, I really didn’t catch what “RowData” at line 57 and 58 stand for. It is a Class, isn’t it? So, what does “.getName()” method return, the name of WHICH RowData? Is “.getName()” declared static? Thanks in advance, great guide!

    Posted on July 31st, 2014 at 12:06 pm Reply | Quote
  • Emil Adjiev Says:

    @Fosco Hi, I had a typo in there and I fixed it. RowData is a simple data class that comes to represent a single item in the list. But I had to get the right RowData object from the list in order to populate the ListView Views. So getName() is a simple getter method for getting the name parameter from RowData object. Check the code again. Thanks for you comment.

    Posted on July 31st, 2014 at 8:58 pm Reply | Quote
  • Dean Blakely Says:

    The RowData class seems to be a central component of this example. Too bad it can’t be provided. You say it’s a simple class that represents a single item in the list – but that’s what ViewHolder appears to be, no? Is RowData just like ViewHolder is except that it has getters for the three fields?

    Posted on August 19th, 2014 at 1:47 am Reply | Quote
  • Emil Adjiev Says:

    @Dean Blakely Hi, For the clarification I will explain what is the difference between the ViewHolder and the RowData classes. The ViewHolder class is a helper Android system class that comes to combine all the views that are used for a specific row in the ListView. Please notice that I specified that it contains the VIEWS of the row. This is done in order to make the ListView items more efficient and to avoid View inflation when you inflated the same Views for the previous row some time before. The RowData class on the other hand is my own class that I created in this example, it can be as simple as:

    public class RowData{
    private String title;
    private boolean isChecked;

    // You will have here getters and setters.
    }

    This class come to holder the DATA for each row in the ListView.

    So to sum it up the ViewHolder holds the Views for each row in the ListView, while the RowData is my data class that holds the Data for each row in the ListView.

    I hope I made it clear now.

    Posted on August 20th, 2014 at 6:36 pm Reply | Quote
  • kri Says:

    @Emil Adjiev

    Can u explain me once how to populate rowDataList please??
    thanks in advance.

    Posted on January 28th, 2015 at 7:47 pm Reply | Quote
  • Emil Adjiev Says:

    @kri Hi, you can pupulate this rowDataList the way you want (for example: by parsing a JSON file of data or even by populating it with a data you want manually). In the end the rowDataList is just an array of POJO objects that come to represent the data you want to display in the list.

    Posted on February 2nd, 2015 at 11:15 am Reply | Quote

Leave a comment