Android RecyclerView Tutorial with Kotlin
In this Android RecyclerView tutorial, learn how to use Kotlin to display datasets of a large or unknown size! By Kevin D Moore.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android RecyclerView Tutorial with Kotlin
30 mins
- Getting Started
- Obtaining The API Keys
- RecyclerView 101
- Why Use a RecyclerView?
- RecyclerView and Layouts
- Creating the RecyclerView
- Laying Out RecyclerView Items
- Adapters for RecyclerView
- Keeping Hold of Your Views
- Assembling The Pieces
- Hooking up the Adapter and RecyclerView
- Adding Scrolling Support
- Layout Changes
- Using ItemTouchHelper
- Where to Go From Here?
Hooking up the Adapter and RecyclerView
This is the moment you’ve been waiting for. Connect your adapter to your RecyclerView and make sure it retrieves photos when created so you can explore outer space — in pictures.
Open MainActivity.kt, and add this property at the top:
private lateinit var adapter: RecyclerAdapter
Next, underneath the assignment of recyclerView.layoutManager
, add the following:
adapter = RecyclerAdapter(photosList)
recyclerView.adapter = adapter
Here you’re creating the adapter, passing in the constructors it needs and setting it as the adapter for your RecyclerView.
Although you connected the adapter, there’s one more thing to do to make sure you don’t have an empty screen.
In onStart()
, underneath the call to super
, add this code:
if (photosList.size == 0) {
requestPhoto()
}
This adds a check to see if your list is empty, and if yes, it requests a photo.
Next, in receivedNewPhoto()
, add the following after photosList.add:
adapter.notifyItemInserted(photosList.size-1)
So it looks like the following:
override fun receivedNewPhoto(newPhoto: Photo) {
runOnUiThread {
photosList.add(newPhoto)
adapter.notifyItemInserted(photosList.size-1)
}
}
Here, you inform the recycler adapter that you added an item after updating the list of photos.
Run the app, load up the emulator and before long, Galacticon should look something like this:
That’s not all. Tap on the photo, and you should see a new activity that brings that item into focus:
That’s still not all! Try rotating your device or emulator (Function-Control-F11 or F12) and you’ll see the image in full screen glory!
Depending on the size of the image and your device screen it may look a little distorted, but don’t worry about that.
Congratulations! You have a working RecyclerView. Take a journey amongst the stars!
Adding Scrolling Support
If you head back to MainActivity on your device and try to scroll down, you’ll notice something is amiss — your RecyclerView isn’t retrieving any new photos.
Your RecyclerView is doing as it’s told by showing the contents of photosList
. The problem is the app will only retrieve one photo. It has no idea when or how to grab more.
To remedy this, retrieve the number of the photos and the last visible photo index while scrolling. Then you’ll check to see that the last photo is visible and there are no photos already on request. If these are both true, then your app downloads more pretty photos!
This patch will require a spacewalk, so break out your spacesuit and get ready for a zero-gravity experience.
In MainActivity.kt, add this property with custom accessor below to MainActivity:
private val lastVisibleItemPosition: Int
get() = linearLayoutManager.findLastVisibleItemPosition()
This uses your RecyclerView’s LinearLayoutManager to get the index of the last visible item on the screen.
Next, add a method inside of MainActivity that inserts an onScrollListener
to your RecyclerView, so it can get a callback when the user scrolls:
private fun setRecyclerViewScrollListener() {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
val totalItemCount = recyclerView.layoutManager!!.itemCount
if (!imageRequester.isLoadingData && totalItemCount == lastVisibleItemPosition + 1) {
requestPhoto()
}
}
})
}
This function gives the RecyclerView a scroll listener triggered by scrolling. While scrolling, the listener retrieves the count of the items in its LayoutManager and calculates the last visible photo index. Once done, it compares these numbers, incrementing the index by one because the index begins at zero while the count begins at one. If they match and there are no photos already on request, you request a new photo.
Finally, hook everything to the RecyclerView by calling this method from onCreate
beneath where you set your RecyclerView Adapter:
setRecyclerViewScrollListener()
Build and run the app again. Scroll down, and you should see quite an improvement!
Excellent work! Your RecyclerView now updates to show the latest photo requested by your app. The great thing is that receivedNewPhoto()
handles most of the work because you told it to notify your adapter about new items.
That earns an intergalactic thumbs up for upcycling code!
Layout Changes
Now that your RecyclerView is up and running, it’s time to trick out your spaceship.
Wouldn’t it be cool if your RecyclerView could change its layout? Good news: RecyclerView’s item positioning is separated into a layout manager.
Add a property for a GridLayoutManager to the top of MainActivity.kt:
private lateinit var gridLayoutManager: GridLayoutManager
Note that GridLayoutManager is built-in, but you can easily customize it.
In onCreate()
, initialize the LayoutManager below the existing LinearLayoutManager:
gridLayoutManager = GridLayoutManager(this, 2)
As with the previous LayoutManager, pass in the context the manager will appear in. Unlike the former, it takes an integer parameter. In this case, set the number of columns the grid will have.
Add this method to MainActivity:
private fun changeLayoutManager() {
if (recyclerView.layoutManager == linearLayoutManager) {
//1
recyclerView.layoutManager = gridLayoutManager
//2
if (photosList.size == 1) {
requestPhoto()
}
} else {
//3
recyclerView.layoutManager = linearLayoutManager
}
}
This code checks to see what LayoutManager your RecyclerView is using, and then:
- If it’s using the LinearLayoutManager, it swaps in the GridLayoutManager.
- It requests a new photo if your grid layout only has one photo to show.
- If it’s using the GridLayoutManager, it swaps in the LinearLayoutManager.
Next, make some changes to lastVisibleItemPosition
to help it handle the new LayoutManager. Make it look like the following:
private val lastVisibleItemPosition: Int
get() = if (recyclerView.layoutManager == linearLayoutManager) {
linearLayoutManager.findLastVisibleItemPosition()
} else {
gridLayoutManager.findLastVisibleItemPosition()
}
Here, you ask the RecyclerView to tell you what its LayoutManager is. Then you ask that LayoutManager to tell you the position of the last visible item.
To use the grid layout, make use of the Options menu button that is already available in the app. Add the following code underneath onStart()
:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_change_recycler_manager) {
changeLayoutManager()
return true
}
return super.onOptionsItemSelected(item)
}
This checks the ID of the item tapped in the menu and then determines what to do about it. In this case, there should only be one ID that will match up, effectively telling the app to go away and rearrange the RecyclerView’s LayoutManager.
You’re ready to go! Load the app and tap the button at the top right of the screen. You’ll begin to see the stars shift: