I'm working on a simple calorie counter app using two fragments and a ViewModel. I'm a beginning and this is a modification of an app I just created for a course (this app is not a homework assignment). It uses ViewModel and has a fragment that collects user input and a fragment that displays the input as a MutableList of MutableLiveData. I would like for the list screen to initially be empty except for a TextView with instructions, and I'd like the instructions to disappear once an entry has been added to the list. My class instructor told me to use an if-else statement in the fragment with the list to achieve this, but it's not working. He didn't tell me exactly where to put it. I tried a bunch of different spots but none of them worked. I don't get errors - just no change to the visibility of the TextView.
Here is the code for the ViewModel with the list:
val entryList: MutableLiveData<MutableList<Entry>>
get() = _entryList
init {
_entry = MutableLiveData<Entry>()
_entryList.value = mutableListOf()
}
fun addEntry(entryInfo: Entry){
_entry.value = entryInfo
_entryList.value?.add(_entry.value!!)
}
}
And this is the code for the observer in the list fragment:
Observer { entryList ->
val entryListView: View = inflater.inflate(R.layout.fragment_entry_list, null, false)
if (entryList.isNullOrEmpty()) {
entryListView.instructions_text_view.visibility = View.VISIBLE
} else {
entryListView.instructions_text_view.visibility = View.GONE
}
entryList.forEach {entry ->
val view: View = inflater.inflate(R.layout.entry_list_item, null, false)
view.date_entry_text_view.text = String.format(getString(R.string.date), entry.date)
view.calories_entry_text_view.text =
view.line_divider
binding.entryList.addView(view)
}
Thanks for any help.
CodePudding user response:
set your viewModel to observe on entry added.
I think you have gotten your visibility toggle in the your if else blocks wrong.
if (entryList.isNullOrEmpty()) {
entryListView.instructions_text_view.visibility = View.GONE // OR View.INVISIBLE
} else {
entryListView.instructions_text_view.visibility = View.VISIBLE
}
CodePudding user response:
Your Observer should get notified of changes to entryList when _entryList has changed. Make sure you are calling addEntry() function to trigger the notification.
CodePudding user response:
I guess you are expecting your observer to get notified of the event when you are adding entryInfo
to your event list (_entryList.value?.add(_entry.value!!)
.
But this won't happen as you are just adding an element to the same mutable list, and as the list reference hasn't changed, live data won't emit any update.
To solve this, you have two options.
- Create a new boolean live data which controls when to show and hide the info text. Set its initial value to
false
, and update it totrue
inaddEntry()
function. - Instead of updating the same mutable list, create of copy of it, add the element and set the entryList.value equal to this new list. This way your observer will be notified of the new list.
Additionally, its generally not a good practice to expose mutable data unless there is no alternative. Here you are exposing a mutable list of Entry and that too in the form of a mutable live data. Ideally, your should be exposing LiveData<List<Entry>>
.
This is one possible implementation of all the points that I mentioned:
private val _entryList = MutableLiveData(listOf<Entry>()) // Create a private mutable live data holding an empty entry list, to avoid the initial null value.
val entryList: LiveData<List<Entry>> = _entryList // Expose an immutable version of _entryList
fun addEntry(entryInfo: Entry) {
_entryList.value = entryList.value!! entryInfo
}
I haven't used the _entry
live data here, but you can implement it the same way.