![Create an activity list](../../img/training/android/android-activity-list.png)
In this lesson, you will learn how to populate a list of room in our empty rooms screen.
When you want to create a list view you should use a RecyclerView widget. This widget is able to manage a large data sets and scrool between elements.
The overall container for your user interface is a RecyclerView object that you add to your layout. The RecyclerView fills itself with views provided by a layout manager that you provide. The views in the list (used to display items) are represented by view holder objects. Each view holder is in charge of displaying a single item with a view.
For example, if your list shows music collection, each view holder might represent a single album. The RecyclerView creates only as many view holders as are needed to display the on-screen portion of the dynamic content, plus a few extra. As the user scrolls through the list, the RecyclerView takes the off-screen views and rebinds them to the data which is scrolling onto the screen.
The view holder objects are managed by an adapter (create by extending RecyclerView.Adapter). This adapter creates view holders as needed. The adapter also binds the view holders to their data. It does this by assigning the view holder to a position.
class RoomsAdapter : RecyclerView.Adapter<RoomsAdapter.RoomsViewHolder>() { // (1)
inner class RoomsViewHolder(view: View) : RecyclerView.ViewHolder(view) { // (2)
val name: TextView = view.findViewById(R.id.txt_room_name)
val currentTemperature: TextView = view.findViewById(R.id.txt_current_temperature)
}
private val items = mutableListOf@LTRoomDto@GT() // (3)
fun setItems(rooms: List<RoomDto>) { // (4)
items.clear()
items.addAll(rooms)
notifyDataSetChanged()
}
override fun getItemCount(): Int = items.size // (5)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoomsViewHolder { // (6)
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_rooms_item, parent, false)
return RoomsViewHolder(view)
}
override fun onBindViewHolder(holder: RoomsViewHolder, position: Int) { // (7)
val roomDto = items[position]
holder.apply {
name.text = roomDto.name
currentTemperature.text = roomDto.currentTemperature?.toString() ?: "?"
}
}
}
(1) an adapter must implement RecyclerView.Adapter wich manage a RecyclerView.ViewHolder
(2) we create a RoomsViewHolder which is able to hold fields defined in layout activity_rooms_item.xml. When you scroll through the list view, system does not recreate these fields. It will update the values via method (7)
(3) adapter has a mutable list to store elements to display
(4) method used to update the list content. This method will be called when data will be ready
(5) RecyclerView.Adapter abstract class asks you to implement a first method that returns the number of records
(6) RecyclerView.Adapter abstract class asks you to implement a second method used to initialize a ViewHolder
we inflate activity_rooms_item.xml layout
we send it to ViewHolder constructor
(7) RecyclerView.Adapter abstract class asks you to implement a last method to define what to do when position in the list changes
We will update the empty component created in last session called room list. We will add a Recycler view inside
Open res > layout > activity_rooms.xml and delete the TextView
In Containers palette select a RecyclerView widget and drag into your layout below your welcome message.
This RecyclerView widget should have these properties
id : list_rooms
margins : 16dp Apply a top, right and left margin
layout_width : widget should take all the width (0dp or match_parent)
layout_height : widget should take all the height (0dp or match_parent)
Each line in the RecyclerView
is displayed in its own layout.
Select res > layout right click and choose New > Layout resource file
Name your future layout activity_rooms_item.xml
In Component Tree panel (below Palette panel) select ConstraintLayout (the main viewgroup) and update property layout_height to wrap_content. If you don’t, the view will always fill all the available height on its parent (our recyclerview) and you will still only see one element in your list and others will be hidden.
Add 3 Textviews : one for the room name, one for the current temperature label and a last one for the current temperature value
You should have this rendering
You can use these properties on your TextFields
Room name
id : txt_room_name
marginStart : 16dp
marginTop : 16dp
marginEnd : 16dp
layout_width : 0dp to have the name on all the width
textStyle : bold
textAppearance : @style/TextAppearance.AppCompat.Large
text : empty
Current temperature label put this element under the room name on the left
id : txt_current_temperature_label
marginStart : 16dp
marginBottom : 16dp
marginTop : 8dp
marginEnd : 16dp
layout_width : wrap_content
text : @string/room_current_temperature
Current temperature value put this element under the room name on the right of the label
A last TextView to display window room
id : txt_current_temperature_value
marginStart : 16dp
marginTop : 8dp
marginEnd : 16dp
layout_width : 0dp
text : empty
As we have seen in previous chapter, an adapter manages the view holder objects. The adapter also binds the view holders to their data. It does this by assigning the view holder to a position.
Create a new package com.automacorp.adapter
Create inside a new class called RoomsAdapter
You can copy the example done higher in the first chapter
We need to initialize the recycler view
class RoomsActivity : BasicActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_rooms)
val roomsAdapter = RoomAdapdter()
findViewById@LTRecyclerView@GT(R.id.list_rooms).also { recyclerView -> // (1)
recyclerView.layoutManager = LinearLayoutManager(this) // (2)
recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) // (3)
recyclerView.setHasFixedSize(true) // (4)
recyclerView.adapter = roomsAdapter // (5)
}
roomsAdapter.setItems(RoomService.ROOMS) // (6)
}
}
(1) we find the recycler view defined in layout by its id list_rooms
(2) we have to define the default layout manager (the object which will be used to display a layout on each line)
(3) you can add a line between each line
(4) this indicator help the RecyclerView to optimize the display when all lines have the same size
(5) the adapter is linked to the RecyclerView
(6) data are sent to the adapter to display the lines
You can now launch your app. You should be able to see the default rooms
At this step we have a list of rooms. We would now open the detail of a room when the user clicks on an item in the list.
You need to define an interface which defines a method called when a user clicks on an element
interface OnRoomClickListener {
fun selectRoom(id: Long)
}
RoomsActivity will implement this interface and it will able to call Roomctivity
and send it the given id
RoomsAdapter will receive an instance of this interface (ie RoomsActivity) and call the method when a user will click on a line
We will implement the contract and create an Intent to open a `RoomActivity`
class RoomsActivity : BasicActivity(), OnRoomClickListener { //... override fun selectRoom(id: Long) { val intent = Intent(this, RoomActivity::class.java).putExtra(MainActivity.ROOM_ID_PARAM, id) startActivity(intent) } }
An instance of our interface OnRoomClickListener
is sent in the adapter constructor, and we can call the method in a Click Event listener. In Android, you can interact with different all user events, each object has its own events….
class RoomsAdapter(val listener: OnRoomClickListener): RecyclerView.Adapter@LTRoomsAdapter.RoomsViewHolder@GT() {
// ...
override fun onBindViewHolder(holder: RoomsViewHolder, position: Int) {
val roomDto = items[position]
holder.apply {
name.text = roomDto.name
currentTemperature.text = roomDto.currentTemperature?.toString() ?: "?"
itemView.setOnClickListener { listener.selectRoom(roomDto.id) } // (1)
}
}
override fun onViewRecycled(holder: RoomsViewHolder) { // (2)
super.onViewRecycled(holder)
holder.apply {
itemView.setOnClickListener(null)
}
}
}
(1) listener is called when someone clicks on an item
(2) it’s very important to clear OnClickListener when a view holder is recycled to prevent memory leaks
Don’t forget to update RoomsAdapter
constructor val adapter = RoomsAdapter(this)
in RoomsActivity
You can also update the android:parentActivityName
of the RoomActivity
to return on the list after a back button
If you want more explanations about RecyclerView you can read this codelabs made by Google