ListView is a ViewGroup that displays a list of vertically scrollable items. The list items are automatically inserted into the list using an adapter
that is connected to a source, such as an array or a database query, and each item is converted into a row in the ListView.
BaseAdapter
BaseAdapter, as it’s name implies, is the base class for so many concrete adapter implementations on Android. It is abstract and therefore, cannot be directly instantiated.
Using the BaseAdapter
To use the BaseAdapter with a ListView, a concrete implementation the BaseAdapter
class that implements the following methods must be created:
int getCount()
Object getItem(int position)
long getItemId(int position)
View getView(int position, View convertView, ViewGroup parent)
Before we create our custom BaseAdapter
implementation, we need to create the layout for the ListView row and also a model for the items in the ListView.
Create model class
Each of our ListView rows will conatain an Person name
, image and birthDate
, so our Model class is as follows:
Person.kt
1 2 3 |
class Person(var name: String, var imageID: Int, var birthDate: String) |
ListView row and activity_main layouts
The xml file for the rows of the listview created in the res/layout
folder is shown below:
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/mainLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <ListView android:id="@+id/listViewItems" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@android:color/black" android:dividerHeight="1dp" /> </LinearLayout> |
listview_item.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?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="match_parent" android:orientation="horizontal" android:padding="8dp"> <ImageView android:id="@+id/image" android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_launcher_foreground" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tvName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:text="Ä°sim" android:textColor="@color/black" android:textSize="30dp" /> <TextView android:id="@+id/tvBirth" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:text="yyyy.aa.gg" android:textColor="@color/teal_700" android:textSize="15dp" /> </LinearLayout> </LinearLayout> |
Create the custom BaseAdapter implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.ImageView import android.widget.TextView class CustomListAdapter ( private val context: Context, private val arrayList: ArrayList<Person>) : BaseAdapter() { override fun getCount(): Int { return arrayList.size } override fun getItem(i: Int): Any { return arrayList[i] } override fun getItemId(i: Int): Long { return i.toLong() } override fun getView(i: Int, convertView: View?, viewGroup:ViewGroup?): View? { var convertView: View? = convertView if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.listview_item, viewGroup, false) } val person = getItem(i) as Person val tvName = convertView!!.findViewById(R.id.tvName) as TextView val tvBirth = convertView!!.findViewById(R.id.tvBirth) as TextView val image = convertView!!.findViewById(R.id.personImage) as ImageView tvName.text = person.name tvBirth.text = person.birthDate image.setImageResource(person.imageID) return convertView } } |
Using the custom Adapter
The adapter can simply be used by instantiating it with the required paramters and set as the listview’s adapter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import android.os.Bundle import android.util.Log import android.view.View import android.widget.ListView import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { lateinit var listview: ListView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.v("TAG", "index=") setContentView(R.layout.activity_main) title = "Code4Example.Com" val listView = findViewById<View>(R.id.listViewItems) as ListView val person1 = Person("Tom", R.drawable.res1, "1978.01.01") val person2 = Person("Eliza", R.drawable.res1, "1983.02.02") val person3 = Person("Jannah", R.drawable.res1, "1984.04.04") val person4 = Person("Fasik", R.drawable.res1, "1980.06.06") val person5 = Person("Rustem", R.drawable.res1, "1988.05.05") val arraylist = ArrayList<Person>() arraylist.add(person1) arraylist.add(person2) arraylist.add(person3) arraylist.add(person4) arraylist.add(person5) val adapter = CustomListAdapter(this, arraylist) listView.adapter=adapter } } |
Output:
ListView Optimization
To optimize the listview’s performance, a new row layout should be inflated only when convertView == null
. This is because the adapter’s getView
method is called whenever the listview needs to show a new row on the screen. The convertView
gets recycled in this process. Hence, the row layout should be inflated just once when convertView == null
, and it contents should be updated on subsequent getView
calls, instead of inflating a new row on each call which is very expensive.