如何使用 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&lt;String, Integer&gt; 并查看 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

如何在不使用 Firebase 身份验证的情况下保护 Firebase 存储? (下一个)

我想设置firebase实时数据库或条件语句

如何将 Firebase 身份验证链接到实时数据库?