WARNING
This will be a loooong post, so take your time to read through it.
The following code samples are written in Kotlin and I will asume that you are a bit familiar with Android Studio and the project structure of a basic Android app.
If you are currently learning Android development, chances are that you will come across with ListView
. Which is a special kind of View
used for displaying a list of items
(I can already hear you thinking: "duh!") that is quite common in Android.
Although, it is being replaced by RecycleView
because of performance and animations limitations that the ListView
faces.
But it's good for you to be at least familiar with how it works, as it might also help you understand why and how RecycleView
works later on.
Requirements
- Java SDK.
- Android Studio (I'll be using version 3.0.1).
- Kotlin (Android Studio 3.0 or later comes with it built-in).
-
Kotlin Android Extensions (so we don't have to use
findViewById()
anymore)
Setup the layout
Head over to your activity_main.xml
, which should initially look like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.chrisvasqm.gettingtoknowlistview.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
The default layout consists of a TextView
on the center of the screen via constraints. Cool, but we won't need that, so go ahead and 🗑️ it.
We will replace everything with:
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Now if you check your Android Studio's Preview panel, it should look like this:
Great! We can see that the list is already showing some content.
So, let's go ahead and try it out. Run the app on your device/emulator.
Got a blank screen? Awesome! That's what is expected 👍.
Why?
Because the ListView
shows those sample items so you can see how it would look when you fill it with data, but we haven't done that... yet.
Adding data to the ListView
There are various ways to achieve this, I'll show you all 3 of them (unless there's more and I'm not aware of them).
1) Using a string-array resource file
The simplest way to fill a ListView
is by using a string-array
resource file.
Head over to your app/res/values/
folder and create a new XML file, let's call it arrays.xml
, then add the following code to it:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="list_items">
<item>One</item>
<item>Two</item>
<item>Three</item>
<item>Four</item>
<item>Five</item>
</string-array>
</resources>
In order to add it our ListView
, we can do it right inside the activity_main.xml
layout by adding the the entries
attribute to it:
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/list_items"/>
Now run the app again and you should be able to see this:
2) Using an ArrayList & ArrayAdapter
In order to do this, we'll need to add an id
attribute to our ListView
, I'll name it "listView":
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then, head over to your MainActivity.java
and make a List<>
of String
s:
val items = listOf(
"First",
"Second",
"Third",
"Fourth",
"Fifth"
)
The listOf()
function takes in any number of arguments and returns a List<TYPE>
object of that same type.
Plus, it is immutable. And by that I mean that it does not have any add()
or remove()
methods, like a regular List
would have. In order to get them, you must use the mutableListOf()
method instead.
Now we need to pass in our items
list to an ArrayAdapter object:
val arrayAdapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
items
)
The android.R.layout.simple_list_item_1
is a layout made out of only a TextView
that is part of the android
package that has a R.layout folder, which holds commonly used resources so we don't have to make our own (but we will in our next section).
Now that we have our arrayAdapter
ready, we can set our ListViews
's adapter to it:
listView.adapter = arrayAdapter
Our MainActivity's OnCreate
method should look like this now:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = listOf(
"First",
"Second",
"Third",
"Fourth",
"Fifth"
)
val arrayAdapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
items
)
listView.adapter = arrayAdapter
}
What are you waiting for? go run the app and check it out yourself!
You should be able to see this now:
Well done, but... let's take it to the next level 😎!
3) Using a Custom ArrayAdapter
At the end of this section, we will be doing this:
First thing we will need is to create a class
that will hold our Contact related data:
class Contact(
val imageResource: Int,
val name: String,
val description: String
)
Now we can create our Custom ArrayAdapter by creating a class
that will inherit from the ArrayAdapter class
and since we are going to be displaying Contact
s, I will name it ContactArrayAdapter
:
class ContactArrayAdapter(context: Context, contacts: List<Contact>)
: ArrayAdapter<Contact>(context, 0, contacts) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val rootView = convertView ?: LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
val currentContact = getItem(position)
rootView.contactImage.setImageResource(currentContact.imageResource)
rootView.contactName.text = currentContact.name
rootView.contactDescription.text = currentContact.description
return rootView
}
}
You will probably notice that the R.layout.list_item
will be highlighted with red, that's because we haven't created our custom layout that will show our contactImage
, contactName
and contactDescription
.
Head over to your app/res/layout/
folder, Right Click
on the layout
folder and select New > Layout resource file
. Name it "list_item" and hit OK.
Now add this XML to it:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/contactImage"
android:layout_width="64dp"
android:layout_height="64dp"
app:srcCompat="@mipmap/ic_launcher_round" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="16dp">
<TextView
android:id="@+id/contactName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Contact Name"
android:textColor="@android:color/black"
android:textSize="18sp" />
<TextView
android:id="@+id/contactDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Contact Description" />
</LinearLayout>
</LinearLayout>
Copy our profile images from my profile and Jorge's, and then paste them in your app/res/drawable/
folder.
And now we finally have everything in place to set things in motion.
Go back to your MainActivity
and refactor it so we now have a List<Contact>
and instead of using the ArrayAdapter<String>
we will use our new ContactArrayAdapter
.
Which should look like:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val contacts = listOf(
Contact(R.drawable.contact_chris, "Christian Vasquez", "QA Analyst"),
Contact(R.drawable.contact_david, "Jorge Peña", ".NET Web Developer")
)
val arrayAdapter = ContactArrayAdapter(this, contacts)
listView.adapter = arrayAdapter
}
}
Run the app and you should be able to see our final result:
What? Your images aren't circular?
Oh... I'll leave that as a homework for you :)
Cough cough hint cough cough.
Final words
YOU MADE IT 👏👏👏
Make sure you high five yourself because THAT was a lot of work!
And I hope you learned something new along the way 🤓.