Ok, i'll try to keep it simple. I'm writing an app, and one of its fragments contains a recyclerview which displays the elements inside a Room table. When the table is empty, i should display a placeholder.
I'm quite new to MVVM, but the approach i tried to follow was to add the placeholder to the layout (a simple textview) and removing it from the layout if the table is not empty. The problem is, every attempt to determine if the table is empty didn't work. What i tried was:
So basically i'm struggling a lot to simply remove a textview from a layout when a db is empty while avoiding to break the MVVM principles. Here's some useful code
DAO function
@Query("SELECT * FROM Event LIMIT 1")
fun getAnyEvent(): EventResult?
ViewModel Function
fun getAnyEvent() = viewModelScope.launch(Dispatchers.Default) {
eventDao.getAnyEvent()
}
The first non-working approach
if (homeViewModel.getAnyEvent() != null) {
val homeMain: LinearLayout = v.findViewById(R.id.homeMain)
val placeholder: TextView = v.findViewById(R.id.noEvents)
homeMain.removeView(placeholder)
}
The adapter
class EventAdapter internal constructor(context: Context) : RecyclerView.Adapter<EventAdapter.EventViewHolder>() {
private var events = emptyList<EventResult>() // Cached copy of events
private val appContext = context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
return EventViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.event_row, parent, false))
}
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val current = events[position]
holder.setUpView(event = current)
}
inner class EventViewHolder (view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
private val favoriteButton: ImageView = view.favoriteButton
private val eventPerson: TextView = view.eventPerson
private val eventDate: TextView = view.eventDate
private val eventYears: TextView = view.eventYears
init {
view.setOnClickListener(this)
}
// Set every necessary text and click action in each row
fun setUpView(event: EventResult?) {
val personName = event?.name + " " + event?.surname
val formatter: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
val nextDate = event?.nextDate?.format(formatter)
val nextAge = appContext.getString(R.string.next_age_years) + ": " + (event?.nextDate?.year?.minus(event.originalDate.year)).toString()
eventPerson.text = personName
eventDate.text = nextDate
eventYears.text = nextAge
override fun onClick(v: View?) {
println("test")
}
}
internal fun setEvents(events: List<EventResult>) {
this.events = events
notifyDataSetChanged()
}
override fun getItemCount() = events.size
}
What approach should i use? And what am i doing wrong using this pattern? As a side note, the recyclerview works and i'm already using livedata to properly observe changes.
It seemed you have just one need - to know in your fragment whether your db-query return 0 or more records. Have you tried to use LiveData for this (with no coroutines)? (you mention that, but it's not clear)
fun getAnyEvent(): LiveData<List<EventResult>>
val eventResult: LiveData<List<EventResult>> = eventDao.getAnyEvent()
In your fragment you simply observe your eventResult and you can do whatever you want with your UI according to the list size (if it's 0, then remove your View stub)
homeViewModel.eventResult.observe(viewLifecycleOwner, Observer { eventList -> yourAdapter.setEvents(eventList)})
Here is Google's Codelab with all this stuff, for example