In this tutorial we’ll use a CustomAdapter that populates the custom rows of the Android ListView with an ArrayList
. Also to enhance the user experience, we’ll animate the ListView while scrolling.
The simplest Adapter to populate a view from an ArrayList is the ArrayAdapter
. That’s what we’ll implement in this tutorial. There are other adapters as well, such as the CursorAdapter
which binds directly to a result set from a Local SQLite Database and it uses a Cursor as it’s data source.
As a ListView is instantiated and the rows are populated such that the full height of the list is filled. After that no new row items are created in the memory. As the user scrolls through the list, items that leave the screen are kept in memory for later use and then every new row that enters the screen reuses an older row kept in the memory.
Let’s create a xml layout that presents the items in a row in a customised way. row_item.xml
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="Marshmallow"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/name"
android:layout_marginTop="5dp"
android:text="Android 6.0"
android:textColor="@android:color/black" />
<ImageView
android:id="@+id/item_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@android:drawable/ic_dialog_info" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<TextView
android:id="@+id/version_heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="API: "
android:textColor="@android:color/black"
android:textStyle="bold" />
<TextView
android:id="@+id/version_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="23"
android:textAppearance="?android:attr/textAppearanceButton"
android:textColor="@android:color/black"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout>
In this tutorial we’ll build an application that consists of list of rows displaying text descriptions and an info icon. Clicking the row would display the SnackBar with the text elements of that row. Clicking the info will display a SnackBar with information specific to that row.
We are creating a custom ListView of by subclassing ArrayAdapter with the DataModel as the object. getView() is the method that returns the actual view used as a row within the ListView at a particular position. The content_main.xml
contains the ListView as shown below. content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="https://schemas.android.com/apk/res-auto"
tools:context="com.journaldev.customlistview.MainActivity"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main">
<ListView
android:id="@+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
The data model that is contained in the ArrayList is shown below. DataModel.java
public class DataModel {
String name;
String type;
String version_number;
String feature;
public DataModel(String name, String type, String version_number, String feature ) {
this.name=name;
this.type=type;
this.version_number=version_number;
this.feature=feature;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public String getVersion_number() {
return version_number;
}
public String getFeature() {
return feature;
}
}
The CustomAdapter that populates the DataModel into the ListView is shown below. CustomAdapter.java
public class CustomAdapter extends ArrayAdapter<DataModel> implements View.OnClickListener{
private ArrayList<DataModel> dataSet;
Context mContext;
// View lookup cache
private static class ViewHolder {
TextView txtName;
TextView txtType;
TextView txtVersion;
ImageView info;
}
public CustomAdapter(ArrayList<DataModel> data, Context context) {
super(context, R.layout.row_item, data);
this.dataSet = data;
this.mContext=context;
}
@Override
public void onClick(View v) {
int position=(Integer) v.getTag();
Object object= getItem(position);
DataModel dataModel=(DataModel)object;
switch (v.getId())
{
case R.id.item_info:
Snackbar.make(v, "Release date " +dataModel.getFeature(), Snackbar.LENGTH_LONG)
.setAction("No action", null).show();
break;
}
}
private int lastPosition = -1;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
DataModel dataModel = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.row_item, parent, false);
viewHolder.txtName = (TextView) convertView.findViewById(R.id.name);
viewHolder.txtType = (TextView) convertView.findViewById(R.id.type);
viewHolder.txtVersion = (TextView) convertView.findViewById(R.id.version_number);
viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);
result=convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
result.startAnimation(animation);
lastPosition = position;
viewHolder.txtName.setText(dataModel.getName());
viewHolder.txtType.setText(dataModel.getType());
viewHolder.txtVersion.setText(dataModel.getVersion_number());
viewHolder.info.setOnClickListener(this);
viewHolder.info.setTag(position);
// Return the completed view to render on screen
return convertView;
}
}
In the above code we’ve added a onClickListener
to the ImageView that displays a SnackBar when clicked with a description for the respective row. Also the list rows are animated when scrolled. The two animation xml resource files are given below. down_from_top.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="-100%" android:toYDelta="0%"
android:duration="400" />
</set>
up_from_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
android:shareInterpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%" android:toXDelta="0%"
android:fromYDelta="100%" android:toYDelta="0%"
android:duration="400" />
</set>
The MainActivity.java
where the CustomAdapter is set to the ListView is defined below. Along with that a random ArrayList of DataModel objects is populated. MainActivity.java
public class MainActivity extends AppCompatActivity {
ArrayList<DataModel> dataModels;
ListView listView;
private static CustomAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
listView=(ListView)findViewById(R.id.list);
dataModels= new ArrayList<>();
dataModels.add(new DataModel("Apple Pie", "Android 1.0", "1","September 23, 2008"));
dataModels.add(new DataModel("Banana Bread", "Android 1.1", "2","February 9, 2009"));
dataModels.add(new DataModel("Cupcake", "Android 1.5", "3","April 27, 2009"));
dataModels.add(new DataModel("Donut","Android 1.6","4","September 15, 2009"));
dataModels.add(new DataModel("Eclair", "Android 2.0", "5","October 26, 2009"));
dataModels.add(new DataModel("Froyo", "Android 2.2", "8","May 20, 2010"));
dataModels.add(new DataModel("Gingerbread", "Android 2.3", "9","December 6, 2010"));
dataModels.add(new DataModel("Honeycomb","Android 3.0","11","February 22, 2011"));
dataModels.add(new DataModel("Ice Cream Sandwich", "Android 4.0", "14","October 18, 2011"));
dataModels.add(new DataModel("Jelly Bean", "Android 4.2", "16","July 9, 2012"));
dataModels.add(new DataModel("Kitkat", "Android 4.4", "19","October 31, 2013"));
dataModels.add(new DataModel("Lollipop","Android 5.0","21","November 12, 2014"));
dataModels.add(new DataModel("Marshmallow", "Android 6.0", "23","October 5, 2015"));
adapter= new CustomAdapter(dataModels,getApplicationContext());
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
DataModel dataModel= dataModels.get(position);
Snackbar.make(view, dataModel.getName()+"\n"+dataModel.getType()+" API: "+dataModel.getVersion_number(), Snackbar.LENGTH_LONG)
.setAction("No action", null).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
The output of the application in action is shown below. This brings an end to this tutorial. You can download the final Android ListView Custom Adapter Project from the link below.
Download Android ListView Custom Adapter Project
Reference: API Guide List View
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Hi,I’ve been looking for a decent listview adapter and this seems to be the one.would you mind if I use it in my app?could you please give me a hint on how to saverify it to sharedpreferences?
- N.Anthony
i learn basics …tankhu
- aiman Javid
Thanks, Aiman.
- Anupam Chugh
Nice simple example thanks
- brianoh
Thanks, Brianoh.
- Anupam Chugh
Great ! It works for me. I integrated a SQlite db to populate listview but I am not able to refresh ListV when db is updated. Any hints ???
- simone
Hy In ‘CustomAdapter.java’ it should return variable ‘result’ else the animation will not be seen
- Alex
Thank you , simple and helpful , prof way :)
- Somaia
Thanks, Somaia.
- Anupam Chugh
sir i had a problem related to snackbar class it doesnot import it goes to error help me
- Hitesh
Add the following dependency in your build.gradle compile ‘com.android.support:design:25.3.1’ (or whichever is the latest SDK version you’re using) Thanks
- Anupam Chugh
Very useful example, thanks for sharing. Best regards,
- Juan Manuel
package com.login; import java.io.Serializable; /** * Created by Jai on 6/10/2017. */ public class Pojo implements Serializable { String name; String loc; String usernam; String pass; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLoc() { return loc; } public void setLoc(String loc) { this.loc = loc; } public String getUsernam() { return usernam; } public void setUsernam(String usernam) { this.usernam = usernam; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } }
- jack
Thanks a lot!!
- Hassan
package com.login; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import java.util.ArrayList; public class list extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list); sqlite sqliteObj= new sqlite(list.this); ArrayList arrColle = sqliteObj.getall(); ListView liView = (ListView)findViewById(R.id.lisv); CustonmAdapter cusObj = new CustonmAdapter(list.this,R.layout.adpater_view,arrColle); liView.setAdapter(cusObj); } public class CustonmAdapter extends ArrayAdapter { Context _con; int _layout; ArrayList arrObj; public CustonmAdapter(Context con , int layout, ArrayList arr) { super(con,layout,arr); _con = con; _layout = layout; arrObj = arr; } @Override public View getView(int position,View convertView, ViewGroup parent) { View v =null; LayoutInflater layou =(LayoutInflater)(list.this).getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = layou.inflate(_layout,null); Pojo poObj =arrObj.get(position); TextView tx = (TextView) v.findViewById(R.id.uName); TextView tx1 = (TextView)v.findViewById(R.id.pName); tx.setText(poObj.getUsernam()); tx1.setText(poObj.getPass()); return v; } } }
- sam
viewHolder.txtName.setText(dataModel.getName()); viewHolder.txtType.setText(dataModel.getType()); viewHolder.txtVersion.setText(dataModel.getVersion_number()); viewHolder.info.setOnClickListener(this); viewHolder.info.setTag(position); This snippet flags Null pointer exception when i scroll down.
- sunday
How to load image in custom list view with array of url using picasso library ? In android example - https://www.journaldev.com/10416/android-listview-with-custom-adapter-example-tutorial
- Kishan
Got the answer. Answer :Picasso.with(mContext).load(dataModel.getImageURL()).resize(100, 100).into(viewHolder.info); Thank you.
- Kishan
The problem about all custom listview tutorials are this : What will happen if i want to add more than 1 Strings to get ? I cant use toString() method for more then one String. For example i got 4 Stringss. @Override public String toString(){ return String1; } Will not work for more then one string.What can i do in this situation ?
- Umut Piynar
This was extremely useful to me and my project, thanks very much!
- Lee Davison
what should i do if i wanna replace that button with a spinner how do i implement setOnSelectedItem method without confusing with the listview setonitemclick
- dhia islam
Short and clear article. Thank you.
- baser
Appreciate your gesture. Thanks!
- Anupam Chugh
Thanks for the valuable information I am also the android user and the developer that’s why I always visit your site here I grave more knowledge about the android it helps to improve my skills and knowledge about the android keep posting like this.
- Windows Defender Customer Service
Glad it’s helped you!
- Anupam Chugh
hello, the code didn’t work for me the app crashes immediately
- mary
I like this post, but missing my own template to show ListView XML file. How to do in the correct way this inserted code as PHP uses simple include(file)… Should be created template inside ListView file like XML or we add own template/acticity and insert just ListView. Need help.
- Matjaz
Thank you
- Komil
Thank you . Very nice article.
- Baserocka
hi this good work same work am doing
- NA
can u repost the above code with database connectivity?.It would be helpful.
- Dinesh
Visit this: https://www.journaldev.com/9438/android-sqlite-database-example-tutorial
- Anupam Chugh
Very educational. Code example works great. Thanks!
- Leonard
The code is clear but I didn’t understood why you stored " result=convertView; " because it is never according to the code. An explanation would be appreciated.
- Pavitra Raut
public ObjectListAdapter(@NonNull Context context, int resource, int textViewResourceId, @NonNull ArrayList arrayList) I’m prompted that I have to use the above constructor, and when I do the app crashes with the following error java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView
- ahmed nabil
How can I pass the data from the custom adapter to the main activity when the imageview is clicked? Thank you
- Chelsea Cruz
Very Helpful… Thanks for the code
- Suraksha
Thank you! Very helpfull code.
- Vladimir
Please I need help I’m stuck in how I can make the spinner on each row on lthe istvew. I already create the custom arrayadapter class and custom data type class, but I don’t know how I can add the spinner to each row. Any I’m really really appreciate it
- colla
Are you still stuck?
- CodeSlave
Very helpful article indeed.
- CodeSlave
I have a problem with disappearing rows. When I scroll too far and go back there’s nothing left, only empty rows - i see their borders when I turn on “show layout borders” (or sth like this - I use other language in smartphone). What could go wrong? I use Android 9 on Nokia 6.1.
- Adrian
Ok, I know, that when I try to hide TextViews in my custom rows by setting textSize or height to 0dp, there are some errors. After scrolling down and up textViews that should be there disappear by setting height or textSize to 0. How should I correctly hide them to remove empty spaces between text lines (row contains a few TextViews placed vertically)?
- Adrian
Thank you very much.
- Joseph Sang
I like this post, but missing my own template to show ListView XML file. How to do in the correct way this inserted code as PHP uses simple include(file)… Should be created template inside ListView file like XML or we add own template/acticity and insert just ListView. Need help.
- Absolute Apk