Android Recycler View

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)
		}
	}

}