Clickable ListView Items with Clickable Buttons
I had a requirement for the project that the main layout displays a list of items and each list item also contains a Button
that reduces or increases the total quantity of that particular item. At the same time clicking on the list item would open a new activity.
- I have created create a
ListView with 4 items
, and used a custom “ArrayAdapter
” to display them.
Android Layout Files:
res/layout/activity_main.xml
<!-- Layout for the main screen -->
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/listview_dessert"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" />
res/layout/list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Layout for a single list item -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="16dp">
<ImageView
android:id="@+id/list_item_icon"
android:layout_width="50dp"
android:layout_height="50dp"/>
<TextView
android:id="@+id/dessert_name" android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/dessert_number"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
</LinearLayout>
Java files:
MainActivity.java
package com.example.annascott.buttondemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create an ArrayList of Dessert objects
ArrayList<Dessert> desserts = new ArrayList<Dessert>();
desserts.add(new Dessert("Donut", 0, R.drawable.doughnut));
desserts.add(new Dessert("Cookie", 0, R.drawable.cookie));
desserts.add(new Dessert("PieceOfCake", 0, R.drawable.piece_of_cake));
desserts.add(new Dessert("Pastry", 0, R.drawable.pastry));
// Create an {@link DessertAdapter}, whose data source is a list of
// {@link Dessert}s. The adapter knows how to create list item views for each item
// in the list.
DesertAdapter flavorAdapter = new DesertAdapter(this, desserts);
// Get a reference to the ListView, and attach the adapter to the listView.
ListView listView = (ListView) findViewById(R.id.listview_dessert);
listView.setAdapter(flavorAdapter);
}
}
Dessert.java
package com.example.annascott.buttondemo;
/**
* {@link Dessert} represents type of desert.
* Each object has 3 properties: name, number, and image resource ID.
*/
public class Dessert {
// Name of the desert
private String mDessertName;
// Number of desserts
private int mDessertNumber;
// Drawable resource ID
private int mImageResourceId;
/*
* Create a new dessert object.
*
* @param vName is the name of the dessert
* @param vNumber is the corresponding number of desserts
* @param image is drawable reference ID that corresponds to the dessert
* */
public Dessert(String vName, int vNumber, int imageResourceId)
{
mDessertName = vName;
mDessertNumber = vNumber;
mImageResourceId = imageResourceId;
}
/**
* Get the name of the dessert
*/
public String getDessertName() {
return mDessertName;
}
/**
* Get the number of desserts
*/
public int getDessertNumber() {
return mDessertNumber;
}
/**
* Get the image resource ID
*/
public int getImageResourceId() {
return mImageResourceId;
}
}
DessertAdapter.java
package com.example.annascott.buttondemo;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
/*
* {@link DessertAdapter} is an {@link ArrayAdapter} that can provide the layout for each list
* based on a data source, which is a list of {@link Dessert} objects.
* */
public class DesertAdapter extends ArrayAdapter<Dessert> {
private static final String LOG_TAG = DesertAdapter.class.getSimpleName();
/**
* This is our own custom constructor (it doesn't mirror a superclass constructor).
* The context is used to inflate the layout file, and the list is the data we want
* to populate into the lists.
*
* @param context The current context. Used to inflate the layout file.
* @param desserts A List of Dessert objects to display in a list
*/
public DesertAdapter(Activity context, ArrayList<Dessert> desserts) {
// Here, we initialize the ArrayAdapter's internal storage for the context and the list.
// the second argument is used when the ArrayAdapter is populating a single TextView.
// Because this is a custom adapter for two TextViews and an ImageView, the adapter is not
// going to use this second argument, so it can be any value. Here, we used 0.
super(context, 0, desserts);
}
/**
* Provides a view for an AdapterView (ListView, GridView, etc.)
*
* @param position The position in the list of data that should be displayed in the
* list item view.
* @param convertView The recycled view to populate.
* @param parent The parent ViewGroup that is used for inflation.
* @return The View for the position in the AdapterView.
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Check if the existing view is being reused, otherwise inflate the view
View listItemView = convertView;
if(listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.list_item, parent, false);
}
// Get the {@link Dessert} object located at this position in the list
Dessert currentDesert = getItem(position);
// Find the TextView in the list_item.xml layout with the ID version_name
TextView nameTextView = (TextView) listItemView.findViewById(R.id.dessert_name);
// Get the version name from the current Dessert object and
// set this text on the name TextView
nameTextView.setText(currentDesert.getDessertName());
// Find the TextView in the list_item.xml layout with the ID version_number
TextView numberTextView = (TextView) listItemView.findViewById(R.id.dessert_number);
// Get the version number from the current Dessert object and
// set this text on the number TextView
numberTextView.setText(String.valueOf(currentDesert.getDessertNumber()));
// Find the ImageView in the list_item.xml layout with the ID list_item_icon
ImageView iconView = (ImageView) listItemView.findViewById(R.id.list_item_icon);
// Get the image resource ID from the current Dessert object and
// set the image to iconView
iconView.setImageResource(currentDesert.getImageResourceId());
// Return the whole list item layout (containing 2 TextViews and an ImageView)
// so that it can be shown in the ListView
return listItemView;
}
}
2. Each item was made clickable by adding OnItemClickListener which utilized a switch statement that used intents to open new activities. Each new activity has up button to navigate back to main activity. It was achived by declaring parent activity in AndroidManifest.xml file.
MainActivity.java
package com.example.annascott.buttondemo;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create an ArrayList of Dessert objects
final ArrayList<Dessert> desserts = new ArrayList<Dessert>();
desserts.add(new Dessert("Donut", 0, R.drawable.doughnut));
desserts.add(new Dessert("Cookie", 0, R.drawable.cookie));
desserts.add(new Dessert("PieceOfCake", 0, R.drawable.piece_of_cake));
desserts.add(new Dessert("Pastry", 0, R.drawable.pastry));
// Create an {@link DessertAdapter}, whose data source is a list of
// {@link Dessert}s. The adapter knows how to create list item views for each item
// in the list.
DesertAdapter flavorAdapter = new DesertAdapter(this, desserts);
// Get a reference to the ListView, and attach the adapter to the listView.
ListView listView = (ListView) findViewById(R.id.listview_dessert);
listView.setAdapter(flavorAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//Dessert dessert = desserts.get(i);
switch(i) {
case 0:
Intent donut = new Intent(MainActivity.this, Donut.class);
startActivity(donut);
break;
case 1:
Intent cookie = new Intent(MainActivity.this, Cookie.class);
startActivity(cookie);
break;
case 2:
Intent pieceOfCake = new Intent(MainActivity.this, PieceOfCake.class);
startActivity(pieceOfCake);
break;
case 3:
Intent pastry = new Intent(MainActivity.this, Pastry.class);
startActivity(pastry);
break;
}
}
});
}
}
Cookie.java
package com.example.annascott.buttondemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class Cookie extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cookie);
}
}
Donut.java
package com.example.annascott.buttondemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class Donut extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donut);
}
}
PieceOfCake.java
package com.example.annascott.buttondemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class PieceOfCake extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_piece_of_cake);
}
}
Pastry.java
package com.example.annascott.buttondemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class Pastry extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pastry);
}
}
activity_cookie.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="16dp">
<ImageView
android:src="@drawable/cookie"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
activity_donut.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="16dp">
<ImageView
android:src="@drawable/doughnut"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
activity_piece_of_cake.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="16dp">
<ImageView
android:src="@drawable/piece_of_cake"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
activity_pastry.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="?android:attr/listPreferredItemHeight"
android:padding="16dp">
<ImageView
android:src="@drawable/pastry"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
3. Let’s add some buttons to our list_item.xml
<?xml version="1.0" encoding="utf-8"?><!-- Layout for a single list item -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/list_item_icon"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/dessert_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="-" />
<TextView
android:id="@+id/dessert_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>
However our OnClickItemListener stops working. There is a simple solution to get it back. By adding android:focusable=”false” to buttons’ layouts we restore clickability of the items in the ListView.
4. Last step: Lets hook up our buttons, so we can change items count in ListView. First, we put android:onClick=”Decrement” and android:onClick=”Increment” into layout of our “-” and “+” buttons. We define Increment and Decrement methods in our MainActivity that are tied to every click of every instance of the button, in every row of our ListView. The View view that is being passed in as a parameter is our button.
We get the parent of our button, by casting it as a LinearLayout.
Here the final state of MainActivity.java
package com.example.annascott.buttondemo;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
int desertNumber;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create an ArrayList of Dessert objects
final ArrayList<Dessert> desserts = new ArrayList<Dessert>();
desserts.add(new Dessert("Donut", 0, R.drawable.doughnut));
desserts.add(new Dessert("Cookie", 0, R.drawable.cookie));
desserts.add(new Dessert("PieceOfCake", 0, R.drawable.piece_of_cake));
desserts.add(new Dessert("Pastry", 0, R.drawable.pastry));
// Create an {@link DessertAdapter}, whose data source is a list of
// {@link Dessert}s. The adapter knows how to create list item views for each item
// in the list.
DesertAdapter flavorAdapter = new DesertAdapter(this, desserts);
// Get a reference to the ListView, and attach the adapter to the listView.
ListView listView = (ListView) findViewById(R.id.listview_dessert);
listView.setAdapter(flavorAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//Dessert dessert = desserts.get(i);
switch(i) {
case 0:
Intent donut = new Intent(MainActivity.this, Donut.class);
startActivity(donut);
break;
case 1:
Intent cookie = new Intent(MainActivity.this, Cookie.class);
startActivity(cookie);
break;
case 2:
Intent pieceOfCake = new Intent(MainActivity.this, PieceOfCake.class);
startActivity(pieceOfCake);
break;
case 3:
Intent pastry = new Intent(MainActivity.this, Pastry.class);
startActivity(pastry);
break;
}
}
});
}
public void Decrement(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent();
TextView quantityView = (TextView) parentRow.findViewById(R.id.dessert_number);
String quantityString = quantityView.getText().toString();
desertNumber = Integer.parseInt(quantityString);
desertNumber -= 1;
if (desertNumber < 0) {
desertNumber = 0;
Toast.makeText(MainActivity.this, "Can not be less than 0",
Toast.LENGTH_SHORT).show();}
quantityView.setText(String.valueOf(desertNumber));
}
public void Increment(View view) {
LinearLayout parentRow = (LinearLayout) view.getParent();
TextView quantityView = (TextView) parentRow.findViewById(R.id.dessert_number);
String quantityString = quantityView.getText().toString();
desertNumber = Integer.parseInt(quantityString);
desertNumber += 1;
quantityView.setText(String.valueOf(desertNumber));
}
}
list_item.xml
<?xml version="1.0" encoding="utf-8"?><!-- Layout for a single list item -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/list_item_icon"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/dessert_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:onClick="Decrement"
android:text="-" />
<TextView
android:id="@+id/dessert_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:onClick="Increment"
android:text="+" />
</LinearLayout>
You can find the complete code here:
REFERENCE: https://developer.android.com/guide/topics/ui/layout/listview
https://developer.android.com/reference/android/widget/ListView