We all love scrolling (some weird people swipe ¯_(ツ)_/¯).
Scrolling is a major interaction performed almost in every application around.
Let’s have a look what’s under the hood of Android implementation of “efficient scrolling” called RecyclerView!
Part One: What is Recycler View and what does it actually recycle? ♻️
RecyclerView initiates an efficient way of creating ViewHolder objects, binding them with data and reusing according to a list state.
Imagine, you received 44442
emails on your mobile application (Must be very popular!).
Theoretically, we would think that for every single email there should be a “cell view” created with email details such as title and short description. But we have… a lot of emails… What to do?
RecyclerView comes to help! But how?
It consists from three main elements: cell layout, Adapter and ViewHolder objects.
Cell layout represents an XML design blueprint of a list item. Something like this:
As a next step, Adapter uses a data from the email server, and “binds” XML view to a particular data such as title or delivery time.
And to perform this binding, Adapter manages something which is called ViewHolder - a class which belongs to RecyclerView, responsible for displaying an item in a view.
And here comes amazing part:
Instead of creating 44442 views for each of our emails, RecyclerView reuses already created view holders which left an observable area of the screen.
What?
Imagine, your list of emails occupies full screen height and can contain 10 emails to be showed.
You start scrolling up.
⬆️
Once the top view is out of view, it is being reused by RecyclerView, with new data and pushed as a new bottom element. Something like that.
Enough of theory, let’s do practical part! 🔨
Part Two: Let’s create a RecyclerView with… Kotlin! ♻️
Kotlin is love, Kotlin is life (me ⓒ).
From my point of view, Kotlin is one of the best things which happened to Android :)
It is super easy to write, has a functional approach, easy to understand and hides a lot of boilerplate logic.
What are we waiting for? Let’s create our “email” app project!
NOTE
: All ready copy-paste code is available here
A simple EmptyActivity will be fine for our use case:
Next, let’s create our item/cell layout!
For a simple preview, we can use a title, description and a time.
Let’s create one in res/layout folder named email_item_layout.xml:
Closer look:
<?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="60dp"
android:background="#FFECECEC"
android:orientation="horizontal"
android:weightSum="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="0.2"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/email_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Let's go to Sydney Vivid tonight!"
android:textColor="@color/colorPrimaryDark"
android:textStyle="bold" />
<TextView
android:id="@+id/email_description"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hey, Nick! There is amazing show near..." />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.8"
android:orientation="horizontal">
<TextView
android:id="@+id/email_time"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="10:38" />
</LinearLayout>
</LinearLayout>
Alright, what’s next? Ah, unfortunately RecyclerView doesn’t come by default, so we need to import it as external dependency in our build gradle file:
implementation 'com.android.support:recyclerview-v7:28.0.0'
Once it is done, we can create our email data class! We can create “models” package and declare a simple Email class which would contain title, description and delivery time as String attributes:
data class Email(val title: String, val description: String, val time: String)
Done!
Now one of the most interesting parts: The Mighty Adapter!
Let’s create a file named EmailAdapter in new package called “adapters”.
Our EmailAdapter would accept a list of Email objects, context as well as contain a custom ViewHolder:
class EmailAdapter(private val emailList: List<Email>, private val context: Context) : RecyclerView.Adapter<EmailAdapter.EmailViewHolder>() {
override fun onBindViewHolder(emailViewHolder: EmailViewHolder, index: Int) {
emailViewHolder.titleTextView.text = emailList[index].title
emailViewHolder.descriptionTextView.text = emailList[index].description
emailViewHolder.timeTextView.text = emailList[index].time
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmailViewHolder {
return EmailViewHolder(LayoutInflater.from(context).inflate(R.layout.email_item_layout, parent, false))
}
override fun getItemCount(): Int {
return emailList.size
}
inner class EmailViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val titleTextView: TextView = view.findViewById(R.id.email_title)
val descriptionTextView: TextView = view.findViewById(R.id.email_description)
val timeTextView: TextView = view.findViewById(R.id.email_time)
}
}
onCreateViewHolder
is responsible for inflating XML layout of our email item into an EmailViewHolder
object.
EmailViewHolder
class extends ViewHolder
and references views such as title, description and time TextViews
we declared inside our XML layout, to be used for a data injection inside onBindViewHolder
function.
We are almost done!!!
Now we need to place our RecyclerView XML declaration into activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/email_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
Next step will be to initiate RecyclerView inside MainActivity, and feed some random almost-real email data objects :)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val fakeEmails = generateFakeEmails()
setUpEmailRecyclerView(fakeEmails)
}
private fun setUpEmailRecyclerView(emails: List<Email>) {
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
val emailRecyclerView = findViewById<RecyclerView>(R.id.email_recycler_view)
val recyclerAdapter = EmailAdapter(emails, this)
emailRecyclerView.layoutManager = layoutManager
emailRecyclerView.adapter = recyclerAdapter
}
private fun generateFakeEmails(): List<Email> {
val titles = listOf(
"Hot dogs $1 only!",
"Dev.to beats Medium.",
"We have updated our privacy :/",
"Nick moves to New Zealand")
val descriptions = listOf(
"This is truly amazing, unexpected...",
"Yes, yes, yes! It is happening!",
"Follow our blog to learn more...",
"Well, it supposed to happen...")
val times = listOf(
"13:42",
"16:16",
"12:34",
"20:20")
val emailList = mutableListOf<Email>()
for (i in 0..10) {
emailList.add(
Email(titles.random(), descriptions.random(), times.random())
)
}
return emailList
}
}
OMG! Look what we’ve got!!!
And now we can Recycle our emails 😜
( ^^)o自自o(^^ )
Top comments (2)
Nice one!
RecyclerView is one of the most important widgets in android development (imo of course!)
One small micro-optimisation, you should fetch the Email object once and not for each field you are populating a widget. Imagine having
44442
emails, it could be fairly expensive. Also the bind method is called quite a lot of times especially if the user is scrolling a lot.Thanks a lot for your feedback, Giorgos!!!
I really appreciate it, since do Android exclusively as a hobby which enjoy so much.
I’ll add your fix to the article 👍