Handle multiple recycler views in Android [Kotlin] - android

I have a situation where I have 3 RecyclerView on a single Layout. They are dependent on each other in a certain way. Data is coming from Room Database.
Question Prototype (Problem statement): Let's say you have floors like (Floor1, Floor2, Floor3 etc.) and inside each floor you have rooms like (Room1, Room2, Room3 etc.) and inside each room you have People with name like (PersonA, PersonB, PersonC).
Given constraint is that : A person cannot be in two different rooms at same type.
Edit 1: Floors, Rooms and Persons are coming from the database in the form of a list of strings.
How would you show that using maybe ( a recycler view ) or anything on a single screen layout.
There can be infinite number of floors, rooms and persons. But that information is fetch from a room database.
My approach : (This is not a complete approach), But I am thinking like having one RecyclerView at the top which holds number of floors. We use a query to get the total floors from the database and sort them and display. During the onBindViewHolder() in the adapter for floors we have a condition which checks if there is a room associated with that floor and if there is then we make another query to fetch from database and show it (maybe using a new recycler View) but I don't know how to do that. Is nested recycler view a thing that can be used here.
It doesn't stop here as we make another query to database using that room name to find all the persons inside that room. Which is another recycler view.
I am thinking of applying this approach but I feel there are many stoppage for implementing this. And I am not sure if this is the way to handle such things.
Image for reference :
I am looking for any information if you have been through any such situations then what approach did you followed. Is there any library that can be used to simplify this task or anything that you can provide knowledge of would be helpful. Thanks!
Edit 2: What's working now :
I tried implementing the nested recycler view. Things seems to be working fine as now I have two adapters currently (for floor and rooms) and I am inflating the floor adapter from the fragment and inflating the rooms adapter from onBindViewHolder of FloorsAdapter.
The problem I am facing now that inside recycler view I am getting the list of floors correctly but for the list of rooms(in child recycler view) I am only getting it for last floor.
Check this image for reference (Floor 2 and 1 also have rooms but I am only getting room C1 which is present in last floor which is 3) :
Current code in Adapter :
class FloorsAdapter(
private val controlPanelViewModel: ControlPanelViewModel,
private val activity: FragmentActivity?
) : RecyclerView.Adapter<FloorsAdapter.FloorViewHolder>() {
private var floorList = emptyList<String>()
private lateinit var roomAdapter: RoomAdapter
inner class FloorViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FloorViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListItemControlPanelFloorsBinding.inflate(layoutInflater, parent, false)
return FloorViewHolder(binding.root)
}
override fun onBindViewHolder(holder: FloorsAdapter.FloorViewHolder, position: Int) {
val item = floorList[position]
ListItemControlPanelFloorsBinding.bind(holder.itemView).apply {
Timber.d("Current floor is $item, Floor List is : $floorList")
tvFloor.text = item
roomAdapter = RoomAdapter(controlPanelViewModel, activity)
rvRoomControlPanel.adapter = roomAdapter
rvRoomControlPanel.layoutManager = LinearLayoutManager(activity)
controlPanelViewModel.getAllRooms(item).observeForever(Observer {
Timber.d("List of rooms : $it")
//Finding distinct rooms
val distinct = it.toSet().toList()
Timber.d("Distinct rooms list : $distinct")
roomAdapter.roomList(distinct)
})
}
}
#SuppressLint("NotifyDataSetChanged")
fun floorList(floors: List<String>) {
this.floorList = floors
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return floorList.size
}
}
class RoomAdapter(
private val controlPanelViewModel: ControlPanelViewModel,
private val activity: FragmentActivity?
) : RecyclerView.Adapter<RoomAdapter.RoomViewHolder>() {
private var roomList = emptyList<String>()
inner class RoomViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListItemControlPanelRoomsBinding.inflate(layoutInflater, parent, false)
return RoomViewHolder(binding.root)
}
override fun onBindViewHolder(holder: RoomViewHolder, position: Int) {
val item = roomList[position]
ListItemControlPanelRoomsBinding.bind(holder.itemView).apply {
Timber.d("Current room is $item")
tvRoom.text = item
}
}
#SuppressLint("NotifyDataSetChanged")
fun roomList(room: List<String>) {
this.roomList = room
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return roomList.size
}
}
Code in Fragment for inflating the Floors Adapter:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.rvFloors.adapter = adapter
binding.rvFloors.layoutManager = LinearLayoutManager(requireContext())
controlPanelViewModel.getAllFloors.observe(viewLifecycleOwner, Observer{
Timber.d("List is $it")
//Remove duplicates from received list
val distinct = it.toSet().toList()
Timber.d("List after removing duplicates: $distinct")
adapter.floorList(distinct)
})
Timber.d("Adapter: $adapter" )
}
Edit 3: Going through the logs I found something which explains why is it happening. The list of floors is getting executed first because of this
controlPanelViewModel.getAllFloors.observe(viewLifecycleOwner, Observer{
Timber.d("List is $it")
//Remove duplicates from received list
val distinct = it.toSet().toList()
Timber.d("List after removing duplicates: $distinct")
adapter.floorList(distinct)
})
As we are inflating the rooms recycler view from floors adapter here
roomAdapter = RoomAdapter(controlPanelViewModel, activity)
rvRoomControlPanel.adapter = roomAdapter
rvRoomControlPanel.layoutManager = LinearLayoutManager(activity)
controlPanelViewModel.getAllRooms(item).observeForever(Observer {
Timber.d("List of rooms : $it")
//Finding distinct rooms
val distinct = it.toSet().toList()
Timber.d("Distinct rooms list : $distinct")
roomAdapter.roomList(distinct)
})
It is going to be little delayed as data is being fetched from Room. So instead of doing something like (Floor 1 -> Room1, Room2 we are getting something like (Floor1, Floor2 -> Room3, Room4). The data about rooms for previous floors is getting lost.
What to do here? How to stop the execution of next floors unless we have fetched all the rooms and shown using the textview.

I have tried replicate the exact scenario with one vertical recycler view and horizontal recycler view. Here is the thing, one vertical recyclerview for Floors. Then one in floor item, list of room item views can be added to vertical linear layout container. A single room item view contains horizontal recyclerview for person.
Floor Vertical RV -> Adding Custom Room Items to Vertical LL -> Person
Horizontal RV
Hope it helps.
Activity:
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.mainRv)
recyclerView.apply {
adapter = FloorAdapter(getFloors(), this#MainActivity)
layoutManager = LinearLayoutManager(this#MainActivity)
setHasFixedSize(true)
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
}
}
fun getFloors(): List<Floor> {
val list = ArrayList<Floor>()
for (i in 0 until 6) {
list.add(Floor())
}
return list
}
}
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/mainRv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Adapters:
class FloorAdapter(val floors: List<Floor>, val context:Context) : RecyclerView.Adapter<FloorViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FloorViewHolder {
return FloorViewHolder(LayoutInflater.from(context).inflate(R.layout.floor_item,null))
}
override fun onBindViewHolder(holder: FloorViewHolder, position: Int) {
holder.bindView(floors[position])
}
override fun getItemCount(): Int {
return floors.size
}
}
class PersonAdapter(val persons: List<Person>, val context:Context) : RecyclerView.Adapter<PersonViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PersonViewHolder {
return PersonViewHolder(LayoutInflater.from(context).inflate(R.layout.person_item,null))
}
override fun onBindViewHolder(holder: PersonViewHolder, position: Int) {
holder.bindView(persons[position])
}
override fun getItemCount(): Int {
return persons.size
}
}
View Holders:
class PersonViewHolder(val view:View) : RecyclerView.ViewHolder(view) {
fun bindView(person: Person) {
view.findViewById<TextView>(R.id.person_tv).text = "Person:${person.personId}"
}
}
class FloorViewHolder(val view:View) : RecyclerView.ViewHolder(view){
fun bindView(floor: Floor){
val str = "Floor : ${floor.floorId}"
view.findViewById<TextView>(R.id.floor).text = str
view.findViewById<LinearLayout>(R.id.room_container).let { ll->
ll.removeAllViews()
floor.rooms.forEach{
val myView= LayoutInflater.from(view.context).inflate(R.layout.room_item,null)
myView.findViewById<TextView>(R.id.room).text = "RoomId :${it.roomId}"
myView.findViewById<RecyclerView>(R.id.persons_rv)?.apply {
adapter = PersonAdapter(it.persons,view.context)
layoutManager = LinearLayoutManager(view.context,LinearLayoutManager.HORIZONTAL,false)
setHasFixedSize(true)
}
ll.addView(myView)
}
}
}
}
Data Class:
data class Floor(val floorId: Int = (0..900).random()) {
var rooms = arrayListOf<Room>()
init {
for (i in 0 until 50) {
rooms.add(Room())
}
}
}
data class Room(val roomId: Int = (0..900).random()) {
var persons = arrayListOf<Person>()
init {
for (i in 0 until 200) {
persons.add(Person())
}
}
}
data class Person(val personId: Int = (0..900).random())
floor_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/floor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="#android:color/holo_red_dark"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/room_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/floor"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
person_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textview.MaterialTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:padding="18dp"
android:id="#+id/person_tv"
android:textSize="18sp"
android:layout_margin="10dp"
android:background="#android:color/holo_blue_light"
android:textColor="#color/black"
android:layout_height="wrap_content">
</com.google.android.material.textview.MaterialTextView>
room_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:padding="20dp"
android:layout_height="match_parent">
<TextView
android:id="#+id/room"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/holo_green_dark"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/persons_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="#id/room" />
</androidx.constraintlayout.widget.ConstraintLayout>

Related

onCreateViewHolder() of child recycler view not being called in Android [Kotlin]

I have a nested recycler view structure. The data for the recycler view is coming from Room Database and it follows MVVM architecture with repositories and viewmodels.
I am able to inflate the parent recycler view and get the list on screen but the child recycler view is not being inflated. I can see the value being passed through the adapter by using logs. But that is not being used on onBindViewHolder() and neither onCreateViewHolder() is called
Any help would be appreciated.
My CODE:
Fragment :
class ControlPanelFragment : Fragment() {
private var _binding: FragmentControlPanelBinding? = null
private val binding: FragmentControlPanelBinding get() = _binding!!
private lateinit var controlPanelViewModel: ControlPanelViewModel
//floor adapter
private lateinit var adapter: FloorsAdapter
//rooms adapter
private lateinit var roomAdapter: RoomsAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentControlPanelBinding.inflate(layoutInflater)
controlPanelViewModel = ViewModelProvider(this)[ControlPanelViewModel::class.java]
adapter = FloorsAdapter()
roomAdapter = RoomsAdapter()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.rvFloors.adapter = adapter
binding.rvFloors.layoutManager = LinearLayoutManager(requireContext())
ListItemControlPanelFloorsBinding.inflate(layoutInflater).apply {
rvRoomControlPanel.adapter = roomAdapter
rvRoomControlPanel.layoutManager = LinearLayoutManager(activity)
Timber.d("Inside list item")
}
controlPanelViewModel.getAllFloors.observe(viewLifecycleOwner, Observer {
Timber.d("List is $it")
//Remove duplicates from received list
val distinct = it.toSet().toList().sorted()
Timber.d("List after removing duplicates and sorting: $distinct")
adapter.floorList(distinct)
for (i in distinct) {
controlPanelViewModel.getAllRooms(i).observe(viewLifecycleOwner, Observer { rooms ->
Timber.d("Floor: $i, Rooms: $rooms")
val distinctRooms = rooms.toSet().toList()
roomAdapter.roomList(distinctRooms)
})
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Parent recycler view adapter
open class FloorsAdapter() : RecyclerView.Adapter<FloorsAdapter.FloorViewHolder>() {
private var floorList = emptyList<String>()
inner class FloorViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FloorViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListItemControlPanelFloorsBinding.inflate(layoutInflater, parent, false)
return FloorViewHolder(binding.root)
}
#SuppressLint("NotifyDataSetChanged")
override fun onBindViewHolder(holder: FloorsAdapter.FloorViewHolder, position: Int) {
val item = floorList[position]
Timber.d("Current floor is $item, Floor List is : $floorList")
ListItemControlPanelFloorsBinding.bind(holder.itemView).apply {
Timber.d("Current floor is $item, Floor List is : $floorList")
tvFloor.text = "Floor : $item"
}
}
#SuppressLint("NotifyDataSetChanged")
fun floorList(floors: List<String>) {
this.floorList = floors
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return floorList.size
}
}
Child Recycler view adapter:
class RoomsAdapter() : RecyclerView.Adapter<RoomsAdapter.RoomsViewHolder>() {
private var roomList = emptyList<String>()
inner class RoomsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoomsViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ListItemControlPanelRoomsBinding.inflate(inflater, parent, false)
return RoomsViewHolder(binding.root)
}
override fun onBindViewHolder(holder: RoomsViewHolder, position: Int) {
Timber.d("Room List onBindViewHolder: $roomList")
val item = roomList[position]
ListItemControlPanelRoomsBinding.bind(holder.itemView).apply {
tvRoom.text = item
}
}
override fun getItemCount(): Int {
return roomList.size
}
#SuppressLint("NotifyDataSetChanged")
fun roomList(room: List<String>) {
this.roomList = room
Timber.d("Room List: $roomList")
notifyDataSetChanged()
}
}
Fragment layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.controlPanel.ui.ControlPanelFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="10dp">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="#+id/switchFactory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="60dp"
android:checked="false"
android:text="Factory"
android:textOff="OFF"
android:textOn="ON"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="#+id/spnChooseFloor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="200dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="#+id/switchFactory"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/switchFactory"
app:layout_constraintTop_toTopOf="#+id/switchFactory" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="10dp"
android:elevation="8dp"
app:cardCornerRadius="8dp">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvFloors"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/list_item_control_panel_floors" />
</androidx.cardview.widget.CardView>
</LinearLayout>
List Item for fragment recycler view(parent recycler view)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="vertical">
<TextView
android:id="#+id/tvFloor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="Floor 1"
android:textColor="#color/black"
android:textSize="20sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvRoomControlPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
tools:listitem="#layout/list_item_control_panel_rooms" />
</LinearLayout>
List item for child recycler view
<?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="40dp">
<TextView
android:id="#+id/tvRoom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:text="Conference Room"
android:textSize="20sp" />
</LinearLayout>
Edit 1:
My entity :
data class AddedDevicesInformation(
#ColumnInfo(name = "floor_name")
var floorName: String = "",
#ColumnInfo(name = "room_name")
var roomName: String = "",
#ColumnInfo(name = "machine_name")
var machineName: String = "",
#ColumnInfo(name = "device_name")
var deviceName: String = "",
#ColumnInfo(name = "factory_status")
var factoryStatus: Boolean? = false,
#PrimaryKey(autoGenerate = true)
var id: Int? = null,
) {
}
DAO query :
//returns all rooms associated with a floor
#Query("SELECT room_name from added_device_information where floor_name =:floor")
fun readAllRoomsOnAFloor(floor: String): LiveData<List<String>>
Repository:
class ControlPanelRepository(private val devicesInformationDao: DevicesInformationDao) {
fun getAllRooms(floor: String): LiveData<List<String>> = devicesInformationDao.readAllRoomsOnAFloor(floor)
ViewModel:
class ControlPanelViewModel(application: Application) : AndroidViewModel(application) {
//repository instance
val repository: ControlPanelRepository
//Variable for getting all floors
val getAllFloors: LiveData<List<String>>
init {
val database = PowerManagementDatabase.getDatabase(application)
val dao = database.getAddedDevicesInformationDao()
repository = ControlPanelRepository(dao)
getAllFloors = repository.getAllFloors
}
fun getAllRooms(floor: String): LiveData<List<String>> {
return repository.getAllRooms(floor)
}
}
The problem is that each "floor" (row in the floor RecyclerView) has its own unique "room" RecyclerView, but you never attach an adapter to those room RecyclerViews. Instead, you inflate an unused "floor" layout in the Fragment and attach a room adapter to it. That layout will never be displayed anywhere so its adapter is never used.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.rvFloors.adapter = adapter
binding.rvFloors.layoutManager = LinearLayoutManager(requireContext())
// The problem is this part.
// This view isn't shown anywhere, and the views inflated in FloorsAdapter
// never get a RoomAdapter attached
ListItemControlPanelFloorsBinding.inflate(layoutInflater).apply {
rvRoomControlPanel.adapter = roomAdapter
rvRoomControlPanel.layoutManager = LinearLayoutManager(activity)
Timber.d("Inside list item")
}
Each displayed floor layout is inflated in the floor adapter in onCreateViewHolder and the data for the floor then gets set in onBindViewHolder. This means you need to create a separate RoomsAdapter for each floor and attach it inside the floor adapter somewhere.
This makes accessing the room adapters more difficult with your current pattern, but if you make a Floor object that contains both its name and its list of rooms, then the floor object selected in onBindViewHolder can pass the list of rooms to its RoomAdapter. This means the FloorsAdapter would take a List<Floor> instead of List<String>. Then in the main fragment you only need to pass the list of Floor objects to the floor adapter and it will handle passing room lists on to the room adapters appropriately.
data class Floor(val name: String, val rooms: List<String>) {}
The floor adapter would look something like this:
// modify the ViewHolder to store the binding (so you don't have to
// re-bind the views so often) and store the room adapter for this
// floor. There should be one room adapter per floor.
inner class FloorViewHolder(val binding: ListItemControlPanelFloorsBinding,
val roomAdapter: RoomsAdapter)
: RecyclerView.ViewHolder(binding.root) {}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FloorViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListItemControlPanelFloorsBinding.inflate(layoutInflater, parent, false)
// Create one adapter per floor here
val adapter = RoomsAdapter()
binding.rvRoomControlPanel.adapter = adapter
binding.rvRoomControlPanel.layoutManager = LinearLayoutManager(activity)
return FloorViewHolder(binding, adapter)
}
override fun onBindViewHolder(holder: FloorsAdapter.FloorViewHolder, position: Int) {
// for a given floor, set the rooms on that floor's room adapter
val currentFloor = floorList[position]
holder.roomAdapter.roomList(currentFloor.rooms)
holder.binding.tvFloor.text = "Floor : ${currentFloor.name}"
}
Here's an example of how that might look in the ViewModel, when it constructs the list of Floor objects, and the activity would observe the floorList LiveData. Each Floor contains all the data (including nested data) that it needs to display.
private val floorListLiveData = MutableLiveData<List<Floor>>()
val floorList: LiveData<List<Floor>>
get() = floorListLiveData
fun load() {
viewModelScope.launch {
val floors = mutableListOf<Floor>()
val floorList = dao.readFloors()
for(floorName in floorList) {
val rooms = dao.readRoomsInFloor(floorName)
floors.add(Floor(floorName, rooms))
}
floorListLiveData.postValue(floors)
}
}

Recyclerview item increase height when fetch data again Android Kotlin

I have recyerlView. I loaded data from the server. When it initially loads it height is normal, but when I go to the next activity through item click. And come back it slowly increasing the height of the child. I tried to debug this and found that onResume api call causing the issue. But What I am doing wrong in layout I don't get it.
FirstLayout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/reyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
FirstActivity.kt
class FirstActivity : BaseActivity() {
lateinit var binding: FirstLayoutActivityLayoutBinding
private val viewModel: FirstViewModel by inject()
private var listAdapter: ListAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupViewModel()
binding = FirstLayoutActivityLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
}
private fun setupViewModel() {
viewModel.livedata.observe(this, { list ->
setupAdapter(list)
})
}
private fun setupAdapter(list: List<String>) {
initializeAdapter(list)
listAdapter?.updateItemsList(list)
binding.recyclerView.apply {
addItemDecoration(HeaderItemDecoration(context))
val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
itemDecorator.setDrawable(ContextCompat.getDrawable(context, R.drawable.divider)!!)
addItemDecoration(itemDecorator)
adapter = listAdapter
}
}
private fun initializeAdapter(list: List<String>) {
listAdapter = ListAdapter(list.toMutableList())
}
override fun onResume() {
super.onResume()
viewModel.fetchItem() // noraml retrofit call
}
}
HeaderItemDecoration is used from this answer.
ListAdapter.kt
class ListAdapter(private val list: MutableList<String>) : RecyclerView.Adapter<Adapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
ListLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bindItem(list[position])
}
override fun getItemCount(): Int {
return list.size
}
inner class MyViewHolder(private val binding: ListLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
fun bindItem(s: String) {
binding.cool.text = s
}
}
}
ListLayout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/root"
android:background="#color/red"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/cool"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I am adding my view to my Youtube Link. Please look at what the issue is. Thanks
I didn't add any logic to increase the height
looks like a problem with multiple ItemDecorations, which are set every time new data is fetched. to be exact: multiple DividerItemDecorations, which adds small padding to the items (for dividing them). note methods name starts with add..., so every time you are adding new, old stays there and every instance of this divider is adding some padding. if you would implement e.g. pull-to-refresh or auto-refresh in background every e.g. 10 secs then every refresh GUI method call (setupAdapter) would add some space without leaving Activity. currently you are fetching data only once, in onResume, so every move-activity-to-foreground action will add one divider (when data will be fetched properly)
move this part of code to onCreate for setting dividers only once, thats proper place for some additional styling by code
binding.recyclerView.apply {
addItemDecoration(HeaderItemDecoration(context))
val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
itemDecorator.setDrawable(ContextCompat.getDrawable(context, R.drawable.divider)!!)
addItemDecoration(itemDecorator)
}
and inside your setupAdapter method set only adapter to RecyclerView, don't style it (multiple times)
private fun setupAdapter(list: List<String>) {
initializeAdapter(list)
listAdapter?.updateItemsList(list)
binding.recyclerView.adapter = listAdapter
}

I'm trying to print a list on the screen using the recyclerview, but the code in the adapter part is not working

MainActivity is as follows.
Nothing comes out on the screen, but I don't know which part is wrong.
How can I add the value of TV_item_name?
And in the log, it's from the DetailViewAdapter of the inner class.
Log.d (logTag,onCreateViewHolder11iscaled") value is also not output, so what is the problem?
package com.example.test_recyclerview
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.test_recyclerview.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
var logTag : String? = "로그 MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
Log.d(logTag,"111 onCreate is called")
binding.mRecyclerView.layoutManager = LinearLayoutManager(this)
val adapter = DetailViewAdapter()
Log.d(logTag,"222 onCreate is called")
binding.mRecyclerView.adapter = adapter
}
override fun onDestroy() {
super.onDestroy()
binding.mRecyclerView.adapter = null
}
inner class DetailViewAdapter : RecyclerView.Adapter<DetailViewAdapter.ViewHolder>() {
private var list = ArrayList<String>()
var logTag : String? = "로그 MainActivity"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailViewAdapter.ViewHolder {
Log.d(logTag,"onCreateViewHolder11 is called")
list = getItemList()
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_custom_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.d(logTag,"onBindViewHolder is called")
for (i in 1 until list.size) {
Log.d(logTag,"onBindViewHolder is called // list[$i] =" + list[i])
holder.tvItem.text = list[i]
}
}
override fun getItemCount(): Int {
return list.size
}
private fun getItemList(): ArrayList<String> {
for (i in 1..8) {
list.add(i, "Item $i")
}
return list
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvItem : TextView = view.findViewById(R.id.tv_item_name)
val cardViewItem : CardView = view.findViewById(R.id.card_view_item)
}
}
}
item_custom_row.xml
<?xml version="1.0" encoding="utf-8"?>
`enter code here`<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="#+id/card_view_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:padding="10dp"
app:cardCornerRadius="5dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="#string/app_name"
android:src="#mipmap/ic_launcher" />
<TextView
android:id="#+id/tv_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColor="#android:color/black"
android:textSize="18sp"
android:textStyle="bold"
tools:text="Item" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/m_RecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The issue is you're assigning list in onCreateViewHolder but it won't be called since your list.size is returning 0.
Rightly pointed out in this answer
Create list outside adapter & pass it from constructor
Change implementation in onBindViewHolder
Following is the complete example for your use case:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
var logTag: String? = "로그 MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
Log.d(logTag, "111 onCreate is called")
binding.mRecyclerView.layoutManager = LinearLayoutManager(this)
val adapter = DetailViewAdapter(getItemList())
Log.d(logTag, "222 onCreate is called")
binding.mRecyclerView.adapter = adapter
}
private fun getItemList(): ArrayList<String> {
val list = ArrayList<String>()
for (i in 1..8) {
list.add("Item $i")
}
return list
}
override fun onDestroy() {
super.onDestroy()
binding.mRecyclerView.adapter = null
}
inner class DetailViewAdapter(private val list: ArrayList<String>) :
RecyclerView.Adapter<DetailViewAdapter.ViewHolder>() {
var logTag: String? = "로그 MainActivity"
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): DetailViewAdapter.ViewHolder {
Log.d(logTag, "onCreateViewHolder11 is called")
val view =
LayoutInflater.from(parent.context).inflate(R.layout.item_custom_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.d(logTag, "onBindViewHolder is called")
holder.tvItem.text = list[position]
}
override fun getItemCount(): Int {
return list.size
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvItem: TextView = view.findViewById(R.id.tv_item_name)
val cardViewItem: CardView = view.findViewById(R.id.card_view_item)
}
}
}
From the code you posted I can see two problems with your code.
First one is that you are trying to populate the list of data from inside the view holder. What happens is the following:
The adapter is created
The adapter wants to know how many viewholders it should create to see how to populate the recycler view.
The adapter calls getItemCount and this method returns 0, since the list hasen't been populated.
Nothing else is called since nothing else has to be executed.
So, to fix this, the easiest way would be to make getItemCount return 8 and you are set. But, a better way to fix this is to instaintiate your list outside of your adapter, in your activity for example, and pass it as a constructor parameter when you initialize your adapter.
The second problem I'm seeing is on the method onBindViewHolder. You are iterating trough the list to set the text and this will cause that for all items you will only set the text as in the last item (item 8). You need to remember that onBindViewHolder is a method that is called when a view holder needs to refresh it contents because it is going to be used to display a different item of the list, that's why this method is passed the position as parameter, so you can do something like:
holder.tvItem.text = list[position]
** As a side note for the second issue, a more general approach I have seen and used to render the contents of the view holder, is to create a public method called "bind" which is passed the item that needs to be rendered and on the view holder you will have the logic on how to paint it. Something like:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(list[position])
}
/// Inside the view holder
fun bind(item: String) {
tvItem.text = item
}

How to update the text of all EditTexts elements in a RecyclerView with two-way data binding

I have an RecyclerView which holds some CardViews and each CardView contains EditText which multiplies the user given amount times a specific rate (the rate comes from an endpoint and the rate is different per row). For the CardViews I am using data binding.
Use case of the app:
The app should show how much for example 2, 7.89, 14.34, or 110 € are in other currencies.
User enters an amount (in the EditText) in any line, each line has a "rate" (rate comes from an API endpoint) field with a different value
The user entered amount gets multiplied by the "rate"
Each row in the RecyclerView should be updated
Now is the question how to update the text of all EditTexts elements in a RecyclerView with two-Way data binding
This is my data class for data binding:
data class CurrencyItem(
var flag: String,
var shortName: String,
var fullName: String,
var rate: Double
) : BaseObservable() {
#Bindable
var rateTimesAmount: String = (CurrencyApplication.userEnteredAmount * rate).toString()
set(amount) {
val amountAsDouble = amount.toDouble()
val number2digits: Double = String.format("%.2f", amountAsDouble).toDouble()
CurrencyApplication.userEnteredAmount = number2digits
field = number2digits.toString()
notifyPropertyChanged(BR.rateTimesAmount)
}
}
This is my EditText in the item_currency.xml
<EditText
android:id="#+id/currency_rate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:imeOptions="actionDone"
android:inputType="numberDecimal"
android:lines="1"
android:maxLength="8"
android:text="#={currency.rateTimesAmount}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1183.068" />
This is my Application class which stores the user entered amount:
class CurrencyApplication : Application() {
companion object {
var userEnteredAmount: Double = 1.00
}
}
Here I access the RecyclerView through Kotlin Android Extensions:
recycler_view.apply {
setHasFixedSize(true)
itemAnimator = DefaultItemAnimator()
adapter = currencyAdapter
}
Here is the RecyclerView from activity_main.xml
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="8dp"
android:clipToPadding="false"
android:paddingBottom="8dp"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Here is the Adapter for the RecyclerView:
class CurrencyAdapter(
val currencies: ArrayList<CurrencyItem>
) : RecyclerView.Adapter<CurrencyViewHolder>() {
override fun getItemCount() = currencies.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CurrencyViewHolder {
val itemCurrencyBinding: ItemCurrencyBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_currency,
parent,
false
)
return CurrencyViewHolder(itemCurrencyBinding)
}
override fun onBindViewHolder(holder: CurrencyViewHolder, position: Int) {
holder.itemCurrencyBinding.currency = currencies[position]
}
fun setUpCurrencies(newCurrencies: List<CurrencyItem>) {
currencies.clear()
currencies.addAll(newCurrencies)
notifyDataSetChanged()
}
}
class CurrencyViewHolder(val itemCurrencyBinding: ItemCurrencyBinding) :
RecyclerView.ViewHolder(itemCurrencyBinding.root)
You might probably want to create another Observable in -say- your viewModel or adapter and bind it as a variable to the adapter items.
Like so:
class CurrencyAdapter: RecyclerView.Adapter<...>() {
val rate = ObservableField(0.0)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, viewType, parent, false)
binding.setVariable(BR.rate, rate)
return YourViewHolder(binding.root)
}
}
If you encounter issues with other views eagerly updating the rate variable, try to making a custom data binding adapter to only allow views triggering the updates when they are in focus.
#BindingAdapter("android:textAttrChanged")
fun TextView.setOnTextAttrChangedInFocus(listener: InverseBindingListener) {
addTextChangedListener(afterTextChanged = {
if(isFocused) {
listener.onChange()
}
})
}
(example uses androidx.core extension)
I hope it helps ;)
Check out teanity it might help you figure out some stuff like this faster.

kotlin nested recyclerview add data

We are trying to design a nested recyclerview we get the idea that TWO adapters are used. What we are not understanding is the data source construction. We are using SQLite DB for a data source. Our design is a Parent text field that describes a Department in a grocery store. Like Produce and Liquor with child Items in these Depts being tomatoes avocados and beer.
If we use two Models and two DB Tables how do we associate the child items with the Departments?
We thought about one DB Table with this format record 1 Produce tomatoes record 2 null avocado record 3 Liquor beer. This seems not so smart. So next we thought about JOINS or UNION call to make one new Table out of our two tables one with Dept and the other Items.
How would we lay out the two tables so they associate items with respective departments?
We are also guessing here that our ViewHolder needs to be a Class of its own that talks to the Parent and Child Adapters.
We will post a photo of the design we are trying to emulate (copy)
Our questions are how to design the DB Tables?
Do we need a ViewHolder Class that interfaces with the two Adapters?
How to create the two tables and what type of JOIN to call to make a new table?
We have looked at this link and the idea is great but his code does not have the same data source. One is date the other could be SQLite Kotlin Nested
OK we have a working DB and the Two Adapters DeptAdapter and ItemAdapter work BUT not at the same time. The two tables DEPT_TABLE and ITEM_TABLE have data
The view for the two tables is displayed in ListActivity with a activity_list.xml
The ListActivity CAN NOT provided both table views at one time
What we think is wrong is the recyclerview declared in recyclerview_dept.xml is not involved and all the work or views are being provided by the recyclerview in the activity_list.xml with id rvListActivity
code posted below with ONE QUESTION
class ListActivity : AppCompatActivity() {
private var RecyclerAdapter: DeptAdapter? = null
private var RecyclerAdapter2:ItemAdapter? =null
private var recyclerView: RecyclerView? = null
private var recyclerView2: RecyclerView? = null
private val db = DBHelper(this)
private var deptList:List<DEPT> = ArrayList()
private var itemList:List<ITEM> = ArrayList()
private var linearLayoutManager: LinearLayoutManager? = null
private var linearLayoutManager2: LinearLayoutManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list)
initViews()
}// end onCreate
override fun onResume() {
super.onResume()
initDB()
}
// This is ONLY called when Activity is in onResume state
private fun initDB() {
deptList = db.queryAllDEPT()
//itemList = db.queryAllITEM()
if(deptList.isEmpty()){
title = "No Records in DB"
}else{
title = "Contact List"
}
println("########################################### onSTART")
RecyclerAdapter = DeptAdapter(deptList = deptList, context = applicationContext)
//RecyclerAdapter2 = ItemAdapter(itemList = itemList, context = applicationContext)
(recyclerView as RecyclerView).adapter = RecyclerAdapter
//(recyclerView2 as RecyclerView).adapter = RecyclerAdapter2
}
private fun initViews() {
recyclerView = this.findViewById(R.id.rvListActivity)
RecyclerAdapter = DeptAdapter(deptList = deptList, context = applicationContext)
linearLayoutManager = LinearLayoutManager(applicationContext)
(recyclerView as RecyclerView).layoutManager = linearLayoutManager!!
//recyclerView2 = this.findViewById(R.id.rvListActivity)
//RecyclerAdapter2 = ItemAdapter(itemList = itemList, context = applicationContext)
//linearLayoutManager2 = LinearLayoutManager(applicationContext)
//(recyclerView2 as RecyclerView).layoutManager = linearLayoutManager2!!
}
XML File for above Activity
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ListActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/rvListActivity"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</android.support.v7.widget.RecyclerView>
XML file with additional recyclerview
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:id="#+id/list_new_card">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvDEPT"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginRight="16dp"
android:gravity="center_vertical"
android:text="I am tv tvDept"
android:textColor="#color/color_Black"
android:textSize="18sp"
android:textStyle="bold" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvDEPT"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v7.widget.CardView>
Both Adapters
class DeptAdapter(deptList:List<DEPT>,internal var context: Context):RecyclerView.Adapter<DeptAdapter.DeptViewHolder>() {
private var deptList:List<DEPT> = ArrayList()
init{this.deptList = deptList}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeptViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.recyclerview_dept,parent,false)
return DeptViewHolder(view)
}
override fun getItemCount(): Int {
return deptList.size
}
override fun onBindViewHolder(holder: DeptViewHolder, position: Int) {
val items = deptList[position]
holder.item.text = items.dept
}
inner class DeptViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var item: TextView = view.findViewById(R.id.tvDEPT) as TextView
}
}
Child Adapter if you will
class ItemAdapter(itemList:List<ITEM>,var context: Context):RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
private var itemList:List<ITEM> = ArrayList()
init{this.itemList = itemList}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.recyclerview_item,parent,false)
return ItemViewHolder(view)
}
override fun getItemCount(): Int {
return itemList.size
}
override fun onBindViewHolder(holder:ItemViewHolder, position: Int) {
val items = itemList[position]
holder.item.text = items.gitem
}
inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var item: TextView = view.findViewById(R.id.tvITEM) as TextView
}
}
Table Design is simple id and string both in separate Models
Our question how to show both tables in the ListActivity?
So this is really slick code. One issue I have not tied it to a SQLite DB yet.
When I get time to hook it up to a DB I will post an update.
You had a number of the ides correct two adapters is a must
And the List in a List is used here by adding the ChildModel to the ParentModel as a List<ChildModel>
This code belongs to Navendra Jha
You will need to do a little manipulation as he thought it would be fun to have the children scroll left and right independent of the parent so in the MainActivity which displays the data change this line from horizontal to vertical
layoutManager = LinearLayoutManager(this#MainActivity, LinearLayout.VERTICAL, false)
Navendra Jha used an ImageView in his code we commented that out once we understood what was happening under the hood. This is REALLY super charged full blown great code every Kotlin developer will want in her/his tool box

Categories

Resources