I'm writing ToDo list, and I want ListView to display changes, when user adds new taks to the list, or removes them. I'm not sure why, but adapter.notifyDataSetChanged() works only when app uses onCreate() method (turning on, changing to horizontal).
I tried to put adapter.notifyDataSetChanged() everywhere and it does not update content. Only after adding task to database (it shows on Firebase console) and changing to horizontal/restarting you can see new entery in ListView. I also created button, that uses only adapter.notifyDataSetChanged() in onClick() method.
MainActivity.kt :
package com.example.toodoo
import android.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.util.Log
import android.view.View
import android.widget.EditText
import android.widget.ListView
import android.widget.Toast
import com.google.firebase.database.*
class MainActivity : AppCompatActivity(), ItemRowListener {
//Get Access to Firebase database, no need of any URL, Firebase
//identifies the connection via the package name of the app
lateinit var mDatabase: DatabaseReference
var toDoItemList: MutableList<ToDoItem>? = null
lateinit var adapter: ToDoItemAdapter
private var listViewItems: ListView? = null
override fun modifyItemState(itemObjectId: String, isDone: Boolean) {
val itemReference = mDatabase.child(Constants.FIREBASE_ITEM).child(itemObjectId)
itemReference.child("done").setValue(isDone);
}
//delete an item
override fun onItemDelete(itemObjectId: String) {
//get child reference in database via the ObjectID
val itemReference = mDatabase.child(Constants.FIREBASE_ITEM).child(itemObjectId)
//deletion can be done via removeValue() method
itemReference.removeValue()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//reference for FAB
val fab = findViewById<View>(R.id.fab) as FloatingActionButton
listViewItems = findViewById<View>(R.id.items_list) as ListView
//Adding click listener for FAB
fab.setOnClickListener { view ->
//Show Dialog here to add new Item
addNewItemDialog()
}
mDatabase = FirebaseDatabase.getInstance().reference
toDoItemList = mutableListOf<ToDoItem>()
adapter = ToDoItemAdapter(this, toDoItemList!!)
listViewItems!!.setAdapter(adapter)
mDatabase.orderByKey().addListenerForSingleValueEvent(itemListener)
}
private fun addNewItemDialog() {
val alert = AlertDialog.Builder(this)
val itemEditText = EditText(this)
alert.setMessage("Add New Item")
alert.setTitle("Enter To Do Item Text")
alert.setView(itemEditText)
alert.setPositiveButton("Submit") { dialog, positiveButton ->
val todoItem = ToDoItem.create()
todoItem.itemText = itemEditText.text.toString()
todoItem.done = false
//We first make a push so that a new item is made with a unique ID
val newItem = mDatabase.child(Constants.FIREBASE_ITEM).push()
todoItem.objectId = newItem.key
//then, we used the reference to set the value on that ID
newItem.setValue(todoItem)
dialog.dismiss()
Toast.makeText(this, "Item saved with ID " + todoItem.objectId, Toast.LENGTH_SHORT).show()
}
alert.show()
}
var itemListener: ValueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// Get Post object and use the values to update the UI
addDataToList(dataSnapshot)
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Item failed, log a message
Log.w("MainActivity", "loadItem:onCancelled", databaseError.toException())
}
}
private fun addDataToList(dataSnapshot: DataSnapshot) {
val items = dataSnapshot.children.iterator()
//Check if current database contains any collection
if (items.hasNext()) {
val toDoListindex = items.next()
val itemsIterator = toDoListindex.children.iterator()
//check if the collection has any to do items or not
while (itemsIterator.hasNext()) {
//get current item
val currentItem = itemsIterator.next()
val todoItem = ToDoItem.create()
//get current data in a map
val map = currentItem.getValue() as HashMap<String, Any>
//key will return Firebase ID
todoItem.objectId = currentItem.key
todoItem.done = map.get("done") as Boolean?
todoItem.itemText = map.get("itemText") as String?
toDoItemList!!.add(todoItem);
}
}
//alert adapter that has changed
adapter.notifyDataSetChanged()
}
fun onClick(view: View){
adapter.notifyDataSetChanged()
}
}
I think it might be also a problem connected with syncing Firebase data with ListView, but I'm using it first time and I cannot resolve that problem. It's my first time with that database, so mabe I'm missing something out.
You're adding a listener for the data with:
mDatabase.orderByKey().addListenerForSingleValueEvent(itemListener)
Since you're using addListenerForSingleValueEvent
, this only listens for the current data, and then removes the listener. So after loading the current data, you're no longer listening for changes.
To continue to listen for the initial data and changes afterwards, use:
mDatabase.orderByKey().addValueEventListener(itemListener)
Note that you might want to empty toDoItemList
in addDataToList
, as you'll otherwise be adding all existing items again and again whenever there's a change.
Or alternatively use addChildEventListener
, which gives you more granular information on what has changed about the child nodes, so that you perform fine-grained updates to the adapter.