RecyclerView plusAssign Kotlin power

Written by chatikyan | Published 2017/12/13
Tech Story Tags: android | kotlin | recyclerview | software-development | android-development

TLDRvia the TL;DR App

It is common to create lists with recyclerview in almost every project, which take us to create Adapter and ViewHolder for every list. In some cases we go through this process too many times, when there are many lists in our app.

In this article we use Kotlin features to create simple lists in easier and faster way .

Let’s begin with introducing the results of what we will get.

recyclerView.setUp(users, R.layout.item_layout, { nameText.text = it.name surNameText.text = it.surname})

Of course for this magic we also need kotlin android extensions.

Step 1

Let’s create an AbstractAdapter in a generic way

We need to pass to the constructor the actual list of items and layout res id

abstract class AbstractAdapter<ITEM> constructor(protected var itemList: List<ITEM>,private val layoutResId: Int): RecyclerView.Adapter<AbstractAdapter.Holder>() {

**class** Holder(itemView: View) : RecyclerView.ViewHolder(itemView)  

}

Also, we need to create our view holder extending from base ViewHolder and implement our adapter’s onBindViewHolder and onCreateViewHolder as we always do when creating recyclerview.

override fun getItemCount() = itemList.**size

override fun** onCreateViewHolder(parent: ViewGroup,viewType: Int): Holder {val view = parent inflate layoutResIdreturn Holder(view)}

override fun onBindViewHolder(holder: Holder, position: Int) {val item = itemList[position]holder.itemView.bind(item)}

protected open fun View.bind(item: ITEM) {}

class Holder(itemView: View) : RecyclerView.ViewHolder(itemView)

And we also need to create adapter extending from our abstract adapter, don’t panic, it’s shorter, let’s name it Kadapter.

class Kadapter<ITEM>(items: List<ITEM>,layoutResId: Int,private val bindHolder: View.(ITEM) -> Unit): AbstractAdapter<ITEM>(items, layoutResId) {

**override fun** onBindViewHolder(holder: Holder, position: Int) {  
    holder.**itemView**.**bindHolder**(**itemList**\[position\])  
}  

}

Step 2

Let’s see how our advanced Adapter is going to be used:

val adapter = Kadapter(users, R.layout.item_layout) { nameText.text = it.name surNameText.text = it.**surname}**recyclerView.adapter = adapterrecyclerView.layoutManager = LinearLayoutManager(this)

We can just use it in this way, or go deeper. For that let’s move forward and add more stuff.

Superhero landing! He’s going to do a superhero landing!

Let’s create an extension to recycler view.

fun <ITEM> RecyclerView.setUp(items: List<ITEM>,layoutResId: Int,bindHolder: View.(ITEM) -> Unit,manager: RecyclerView.LayoutManager = LinearLayoutManager(this.context)): Kadapter<ITEM> {

**return** Kadapter(items, layoutResId, **{**        bindHolder(**it**)  
**}**)._apply_ **{**        _layoutManager_ \= manager  
    _adapter_ \= **this  
}  

**}

It will encapsulate adapter and layout manager, also we can change layout manager default implementation.

Step 3

Everything was fine so far, but if we try to click the item in the list, nothing will happen. We need to take care about that. Let’s implement onClickListener.

For that we need to add click logic in our abstract adapter. Our onCreateViewHolder becomes this.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {val view = parent inflate layoutResIdval viewHolder = Holder(view)val itemView = viewHolder.itemView itemView.setOnClickListener {val adapterPosition = viewHolder.adapterPosition if (adapterPosition != RecyclerView.NO_POSITION) {onItemClick(itemView, adapterPosition)}}return viewHolder}

protected open fun onItemClick(itemView: View, position: Int) {}

We also need to add onClick logic to our adapter implementation and extensions we created before. The final code of kadapter looks like this:

class Kadapter<ITEM>(items: List<ITEM>,layoutResId: Int,private val bindHolder: View.(ITEM) -> Unit): AbstractAdapter<ITEM>(items, layoutResId) {

**private var itemClick**: ITEM.() -> Unit = **{}  

constructor**(items: List<ITEM>,  
            layoutResId: Int,  
            bindHolder: View.(ITEM) -> Unit,  
            itemClick: ITEM.() -> Unit = **{}**) : **this**(items, layoutResId, bindHolder) {  
    **this**.**itemClick** \= itemClick  
}  

**override fun** onBindViewHolder(holder: Holder, position: Int) {  
    holder.**itemView**.**bindHolder**(**itemList**\[position\])  
}  

**override fun** onItemClick(itemView: View, position: Int) {  
    **itemList**\[position\].**itemClick**()  
}  

}

And final code for extension

fun <ITEM> RecyclerView.setUp(items: List<ITEM>,layoutResId: Int,bindHolder: View.(ITEM) -> Unit,itemClick: ITEM.() -> Unit = {},manager: RecyclerView.LayoutManager = LinearLayoutManager(this.context)): Kadapter<ITEM> {return Kadapter(items, layoutResId, { bindHolder(it)}, { itemClick()}).apply { layoutManager = manageradapter = **this}**}

Now let’s see what we achieved.

We can create adapter without click listener and with click listener.

recyclerView.setUp(users, R.layout.item_layout, { nameText.text = it.name surNameText.text = it.surname})

And with click

recyclerView.setUp(users, R.layout.item_layout, { nameText.text = it.name surNameText.text = it.surname}, { toast("Clicked $name")})

As a bonus we can add base methods to our abstract adapter, such as add, remove, update of course using diffUtils.

The bottom line of this article is demonstrating some of the powerful features of Kotlin + Android, which makes the code more flexible. Now you can go further if interested.

Thanks for reading this article. You can find full code there.

Let’s become friends on Twitter, Github and Facebook. If you enjoyed the writings then please use the clap below to recommend this article so that others can see it.

We will also love to hear your comments and suggestions :)

Thanks.


Published by HackerNoon on 2017/12/13