The old way to display data was ListViews
and GridViews
, but they were very much memory inefficient. RecyclerView
is a much more memory efficient, more advanced version.
RecyclerView without Data Binding (Kotlin Synthetics)
Go to activity_main.xml
and put a new RecyclerView
into the top ConstraintLayout
. For the example, we changed its id to my_recycler_view
.
Now go to MainActivity
. This way of using directly the ID is called Kotlin synthetics. It’s useful to have concise code for smaller projects, instead of using findViewById
or Data Binding. This is not recommended for larger, complex projects. Use Data Binding instead.
Set a layout manager for our recycler view. There’re 3 of them (we may create our own custome layout managers)
- LinearLayoutManager - standard listviews
- GridLayoutManager - display items on a grid
- StaggeredGridLayoutManager - position list items in a saggered grid
Then we set an adapter instance for the recycler view. An adapter class should be a subclass of RecyclerView library’s adapter class. We will set our MyRecyclerViewAdapter
as the adapter of the RecyclerView
.
class MainActivity: AppCompatActivity() {
val fruitsList = listOf(Fruit("Mango", "Tom"), Fruit("Apple", "Mario"), Fruit("Banana", "Carlos"))
override fun onCreate(savedInsanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
my_recycler_view.setBackgroundColor(Color.YELLOW)
// this 2 lines here
my_recycler_view.layoutManager = LinearLayoutManager(this)
my_recycler_view.adapter = MyRecyclerViewAdapter(fruitsList)
}
}
In most practical situations we need to send a list of objects to the adapter and display their values in the recycler view. To do this we need a Data
class.
data class Fruit(val name: String, val supplier: String)
This is our customly created Adapter for our ViewHolder.
An adapter class has three main functions which we need to implement.
Create the resource file we need right click -> new -> android resource file -> file name: list_item; resource type: layout -> OK
. Convert it to a ConstraintLayout.
// this 2 classes go together in a single Kotlin file.
class MyRecyclerViewAdaper(val fruitsList: List<Fruit>): RecyclerView.Adapter<MyViewHolder>() {
// this is where we create the list item xml template.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyviewHolder {
// get layout inflator and using that, inflate the list item.
val layoutInfater = LayoutInflater.from(parent.context)
val listItem = layoutInflater.inflate(R.layout.list_item, parent, false)
return MyViewHolder(listItem)
}
// returns the total number of items in the dataset held by the adapter.
// RecyclerView will create a number of spots for list items considering this value
override fun getItemCoun(): Int {
return fruitsList.size; // we will see 5 rows.
}
// we use this to display data on the list item.
// the position param represents the position between 0 and getItemCount()
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val fruit = fruitsList[position]
holder.bind(fruit)
}
}
// we use this to receive data from onBindViewHolder()
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view) {
fun bind(fruit: Fruit) {
// name_text_view is the name of a TextView we use to show
view.name_text_view.text = fruit.name
}
}
Add a click listener to a Recycler View
We will use a lambda to abstract this function.
On MainActivity
we define a new method which is a Fruit
consumer. Then we pass it to MyRecyclerViewAdapter
as a param arg, and from here to MyViewHolder
where the click listener is defined.
class MainActivity: AppCompatActivity() {
val fruitsList = listOf(Fruit("Mango", "Tom"), Fruit("Apple", "Mario"), Fruit("Banana", "Carlos"))
override fun onCreate(savedInsanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
my_recycler_view.setBackgroundColor(Color.YELLOW)
// this 2 lines here
my_recycler_view.layoutManager = LinearLayoutManager(this)
my_recycler_view.adapter = MyRecyclerViewAdapter(fruitsList, {
selectedFruitItem: Fruit -> listItemClicked(selectedFruitItem)
})
}
private fun listItemClicked(fruit: Fruit) {
Toast.makeText(this@MainActivity,
"Supplier name is ${fruit.supplier}",
Toast.LENGTH_LONG).show()
}
}
// this 2 classes go together in a single Kotlin file.
class MyRecyclerViewAdaper(private val fruitsList: List<Fruit>, private val clickListener:(Fruit)->Unit): RecyclerView.Adapter<MyViewHolder>() {
// this is where we create the list item xml template.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyviewHolder {
// get layout inflator and using that, inflate the list item.
val layoutInfater = LayoutInflater.from(parent.context)
val listItem = layoutInflater.inflate(R.layout.list_item, parent, false)
return MyViewHolder(listItem)
}
// returns the total number of items in the dataset held by the adapter.
// RecyclerView will create a number of spots for list items considering this value
override fun getItemCoun(): Int {
return fruitsList.size; // we will see 5 rows.
}
// we use this to display data on the list item.
// the position param represents the position between 0 and getItemCount()
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val fruit = fruitsList[position]
holder.bind(fruit, clickListener)
}
}
// we use this to receive data from onBindViewHolder()
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view) {
fun bind(fruit: Fruit, clickLisener:(Fruit->Unit)) {
// name_text_view is the name of a TextView we use to show
view.name_text_view.text = fruit.name
view.setOnClickListener {
clickListener(fruit)
}
}
}