使用 Firebase 时,ListView 仅在 onCreate() 方法中更新
ListView updates only in onCreate() method when using Firebase
我正在编写 ToDo 列表,我希望 ListView 在用户向列表中添加新任务或删除它们时显示更改。我不确定为什么,但是
adapter.notifyDataSetChanged()
仅在应用程序使用 onCreate() 方法(打开、更改为水平)时有效。
我试着把
adapter.notifyDataSetChanged() 无处不在,它不更新内容。只有在将任务添加到数据库(它显示在 Firebase 控制台上)并更改为 horizontal/restarting 之后,您才能在 ListView 中看到新条目。
我还创建了按钮,它在 onClick() 方法中仅使用 adapter.notifyDataSetChanged()。
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()
}
}
我认为这也可能是与 ListView 同步 Firebase 数据有关的问题,但我是第一次使用它,我无法解决该问题。
这是我第一次使用该数据库,所以可能我遗漏了一些东西。
您正在为数据添加侦听器:
mDatabase.orderByKey().addListenerForSingleValueEvent(itemListener)
由于您使用的是 addListenerForSingleValueEvent
,这只会侦听当前数据,然后删除侦听器。因此在加载当前数据后,您将不再监听更改。
要继续监听初始数据 和 之后的变化,请使用:
mDatabase.orderByKey().addValueEventListener(itemListener)
请注意,您可能希望在 addDataToList
中清空 toDoItemList
,否则只要有更改,您就会一次又一次地添加所有现有项目。
或者使用 addChildEventListener
,它为您提供有关子节点更改内容的更详细信息,以便您对适配器执行细粒度更新。
我正在编写 ToDo 列表,我希望 ListView 在用户向列表中添加新任务或删除它们时显示更改。我不确定为什么,但是 adapter.notifyDataSetChanged() 仅在应用程序使用 onCreate() 方法(打开、更改为水平)时有效。
我试着把 adapter.notifyDataSetChanged() 无处不在,它不更新内容。只有在将任务添加到数据库(它显示在 Firebase 控制台上)并更改为 horizontal/restarting 之后,您才能在 ListView 中看到新条目。 我还创建了按钮,它在 onClick() 方法中仅使用 adapter.notifyDataSetChanged()。
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()
}
}
我认为这也可能是与 ListView 同步 Firebase 数据有关的问题,但我是第一次使用它,我无法解决该问题。 这是我第一次使用该数据库,所以可能我遗漏了一些东西。
您正在为数据添加侦听器:
mDatabase.orderByKey().addListenerForSingleValueEvent(itemListener)
由于您使用的是 addListenerForSingleValueEvent
,这只会侦听当前数据,然后删除侦听器。因此在加载当前数据后,您将不再监听更改。
要继续监听初始数据 和 之后的变化,请使用:
mDatabase.orderByKey().addValueEventListener(itemListener)
请注意,您可能希望在 addDataToList
中清空 toDoItemList
,否则只要有更改,您就会一次又一次地添加所有现有项目。
或者使用 addChildEventListener
,它为您提供有关子节点更改内容的更详细信息,以便您对适配器执行细粒度更新。