Android ListView with Custom Adapter Example Tutorial

Filed Under: Android

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.

Android ListView Custom Adapter Overview

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.

Recycling Rows

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.

Creating a View template

Let’s create a xml layout that presents the items in a row in a customised way.
row_item.xml


<RelativeLayout xmlns:android="http://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.

Project Structure

android custom listview example, custom adapter in android

Code

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="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://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="http://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="http://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.
android ListView example, custom listview in android

This brings an end to this tutorial. You can download the final Android ListView Custom Adapter Project from the link below.

Reference: API Guide List View

Comments

  1. colla says:

    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

  2. Vladimir says:

    Thank you! Very helpfull code.

  3. Suraksha says:

    Very Helpful.. Thanks for the code

  4. Chelsea Cruz says:

    How can I pass the data from the custom adapter to the main activity when the imageview is clicked? Thank you

  5. ahmed nabil says:

    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

  6. Pavitra Raut says:

    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.

  7. Leonard says:

    Very educational. Code example works great. Thanks!

  8. Dinesh says:

    can u repost the above code with database connectivity?.It would be helpful.

  9. NA says:

    hi this good work same work am doing

  10. Baserocka says:

    Thank you . Very nice article.

  11. Komil says:

    Thank you

  12. Matjaz says:

    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.

  13. mary says:

    hello, the code didn’t work for me the app crashes immediately

  14. 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.

    1. Anupam says:

      Glad it’s helped you!

  15. baser says:

    Short and clear article. Thank you.

    1. Anupam says:

      Appreciate your gesture. Thanks!

  16. dhia islam says:

    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

  17. Lee Davison says:

    This was extremely useful to me and my project, thanks very much!

  18. Umut Piynar says:

    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 ?

  19. Kishan says:

    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

    1. Kishan says:

      Got the answer.
      Answer :Picasso.with(mContext).load(dataModel.getImageURL()).resize(100, 100).into(viewHolder.info);
      Thank you.

  20. sunday says:

    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.

  21. sam says:

    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;
    }
    }
    }

  22. Hassan says:

    Thanks a lot!!

  23. jack says:

    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;
    }
    }

  24. Juan Manuel says:

    Very useful example, thanks for sharing. Best regards,

  25. Hitesh says:

    sir i had a problem related to snackbar class it doesnot import it goes to error help me

    1. Anupam says:

      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

  26. Somaia says:

    Thank you , simple and helpful , prof way 🙂

  27. Alex says:

    Hy
    In ‘CustomAdapter.java’ it should return variable ‘result’
    else the animation will not be seen

  28. simone says:

    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 ???

  29. brianoh says:

    Nice simple example thanks

  30. aiman Javid says:

    i learn basics ….tankhu

  31. N.Anthony says:

    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?

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages