如何使用 Firebase 实时数据库在不知道 Android 索引的情况下更新 ArrayList 中的元素?
Posted
技术标签:
【中文标题】如何使用 Firebase 实时数据库在不知道 Android 索引的情况下更新 ArrayList 中的元素?【英文标题】:How to update an element in ArrayList without knowing index in Android with Firebase Realtime Database? 【发布时间】:2018-06-09 00:12:22 【问题描述】:我有一个简单的 RecyclerView,其中 CardView 作为列表项。从 Firebase 实时数据库检索并存储到 noteList
的列表中的每个项目。 Firebase sdk 为 onChildAdded、onChildChanged 和其他少数几个提供回调。现在我设计了用于回收器视图的适配器以接收 ArrayList。每当实时数据库中的数据发生更改时,我都会使用回调向noteList
添加或删除项目。每当特定项目的值发生更改时,都会调用 onChildChanged
方法。我想在noteList
中反映这种变化并通知适配器。
当然,我们可以天真地搜索 List 中与更改元素的键匹配的每个元素并更新它。但这可以以更好/有效的方式完成吗?
class NoteListAdapter(var data: MutableList<Note>, val onItemClick: (Note) -> Unit) : RecyclerView.Adapter<NoteListAdapter.ViewHolder>()
override fun onBindViewHolder(holder: ViewHolder?, position: Int)
holder!!.bindNoteItem(data[position])
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder
val v = LayoutInflater.from(parent?.context)
.inflate(R.layout.card_note, parent, false)
return ViewHolder(v, onItemClick)
class ViewHolder(v: View, val onItemClick: (Note) -> Unit): RecyclerView.ViewHolder(v)
fun bindNoteItem(note: Note)
...
HomeActivity.kt
class HomeActivity : AppCompatActivity()
lateinit var mDb: DatabaseReference
val noteList: MutableList<Note> = ArrayList()
lateinit var adapter: NoteListAdapter
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
addNoteFAB.setOnClickListener addNote()
mDb = FirebaseDatabase.getInstance().reference
adapter = NoteListAdapter(noteList, this::editNote)
noteListRecycler.layoutManager = LinearLayoutManager(this)
noteListRecycler.adapter = adapter
loadNotes()
fun addNote()
val intent = Intent(this, EditNoteActivity::class.java)
intent.putExtra(EditNoteActivity.EXTRA_NOTE, Note())
startActivity(intent)
fun loadNotes()
val path = "users/" + FirebaseAuth.getInstance().currentUser?.uid + "/notes"
mDb.child(path ).addChildListener(
// onChildAdded
dataSnapshot, prevChildName ->
val note = dataSnapshot?.getValue(Note::class.java)!!
note.id = dataSnapshot.key
noteList += note
adapter.notifyDataSetChanged()
,
// onChildRemoved
dataSnapshot ->
noteList.remove(dataSnapshot?.getValue(Note::class.java))
adapter.notifyDataSetChanged()
,
// onChildChanged, TODO: update the change in elements value in noteList
dataSnapshot, s ->
)
fun editNote(note: Note)
val i = Intent(this, EditNoteActivity::class.java)
i.putExtra(EditNoteActivity.EXTRA_NOTE, note)
startActivity(i)
PS:addChildListener 是一个扩展函数。 (检查代码中的 cmets 以获取回调名称)
由于dataSnapshot
只能告诉我们更改的键和值(据我从文档中知道),我将如何在不知道更改元素索引的情况下更新noteList
?或者真的有办法获取索引吗?
【问题讨论】:
【参考方案1】:到目前为止,我建议您使用专为您的用例构建的 FirebaseUI。
但是,如果您真的想单独使用,那么不,获取更新子项索引的唯一方法是在现有数据中找到它。这是 FirebaseUI 内部的一个示例:
private int getIndexForKey(String key)
int index = 0;
for (DataSnapshot snapshot : mSnapshots)
if (snapshot.getKey().equals(key))
return index;
else
index++;
throw new IllegalArgumentException("Key not found");
FUI uses getIndexForKey(String)
.
只要您不下载大得离谱的列表,遍历列表就性能而言效果很好。否则,您可以使用 Map<String, Integer>
并查看 FUI here 的尝试。
PS:您的 onChildAdded
代码对于插入是错误的(即使项目在中间,也会始终添加到末尾)。看看如何FUI does this。
【讨论】:
谢谢。我将使用 Firebase ui。顺便说一句,我认为列表中的项目附加在末尾(基于时间戳),因此 onChildAdded 中的代码。谢谢指正。 好吧,push()
方法确实根据时间戳创建键,并且默认查询顺序是按字母顺序排列的,但是假设您想要获得最多支持的帖子或其他内容。你可以写query.orderByChild("votes")
,然后无论他们的密钥如何,孩子都可以添加到任何地方。我只是让你免于未来的痛苦。 ?以上是关于如何使用 Firebase 实时数据库在不知道 Android 索引的情况下更新 ArrayList 中的元素?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不影响实时功能的情况下为 React 和 React-native 托管通用的 Firebase 后端代码?
如何使用 ASP.NET MVC 应用程序在 firebase 实时数据库中设置数据
如何在实时 Firebase 数据库上侦听以通过后端 laravel 和 websocket 动态获取数据,而不使用 javascript