You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase填充ListView出现大量重复项求助及代码优化建议

解决Firebase填充ListView时的重复项问题及代码优化

嘿,别着急!我帮你梳理下问题根源,一步步解决这个重复项的困扰:

一、重复项的核心原因:Adapter中的orig集合错误填充

你在CustomAdapter的getView方法里,每次绘制列表项都会执行orig.addAll(listItem)——这意味着列表每滚动一次,orig就会被重复填充一遍相同的数据。随着滚动次数增加,orig里的重复数据会越来越多,搜索时自然会生成大量重复项。

修复步骤:

  1. 将orig的初始化移到Adapter构造方法中,只执行一次:
class CustomAdapter(private var listItem : ArrayList<Menu.ListItem>, private val context: Activity) : BaseAdapter() {
    var orig = ArrayList<Menu.ListItem>()

    // 构造方法里初始化orig,仅执行一次
    init {
        orig.addAll(listItem)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        // 移除这里的orig.addAll(listItem)!!!
        println("List Item is"+listItem)
        println("List Item is"+orig)
        var holder = ViewHolder()
        var customView = convertView
        var flag = true
        // ... 剩余getView代码保持不变
    }

    // ... 其他方法不变
}
  1. 完善搜索过滤逻辑:当前filter方法找到匹配项后没有添加到listItem,导致搜索后列表无结果,同时补上数据更新通知:
fun filter(query : String) {
    println("called")
    listItem.clear()
    val search = query.toLowerCase(Locale.getDefault())
    if(search.isEmpty()) {
        listItem.addAll(orig)
        println("Inside size 0")
    } else {
        for(result in orig) {
            // 可选:如果不想搜索分类标题,可添加!result.isHeader()判断
            if(result.getName().toLowerCase().contains(search)) {
                println("Result is "+result.getName())
                listItem.add(result) // 必须添加到listItem才会显示
            }
        }
    }
    notifyDataSetChanged() // 通知适配器更新UI
}

二、Firebase数据获取的优化问题

  1. 避免重复清空和数据覆盖:
    当前getData用了addValueEventListener(实时监听),每次数据库变化都会遍历所有分类并调用getDataFromKey,而getDataFromKey里的listItemArrayList.clear()会清空之前的分类数据,最后只显示最后一个分类的数据。修改如下:
private fun getData() {
    println("getData")
    val menucatRef = adminRef!!.child("MenuItem")
    menucatRef.addValueEventListener(object : ValueEventListener{
        override fun onCancelled(p0: DatabaseError) {
            Log.e("FirebaseError", "数据获取失败:${p0.message}")
        }

        override fun onDataChange(p0: DataSnapshot) {
            // 每次数据变化时先清空整个列表,避免重复累加
            listItemArrayList.clear()
            for(Snap in p0.children) {
                println("wtfff")
                println("Child count: ${Snap.childrenCount}")
                getDataFromKey(Snap.key.toString(),Snap.childrenCount.toInt())
            }
        }
    })
}

然后修改getDataFromKey,移除内部的listItemArrayList.clear():

private fun getDataFromKey(key: String, child :Int) {
    // 移除这行:listItemArrayList.clear()
    println("called")
    var count = 0
    val Category = CategoryHeader()
    val menuRef = adminRef!!.child("MenuItem").child(key)
    menuRef.addListenerForSingleValueEvent(object : ValueEventListener{
        override fun onCancelled(p0: DatabaseError) {
            Log.e("FirebaseError", "分类数据获取失败:${p0.message}")
        }

        override fun onDataChange(p0: DataSnapshot) {
            val catKey = p0.key.toString()
            for(snap in p0.children) {
                if(count == 0) {
                    Category.setheader(catKey)
                    listItemArrayList.add(Category)
                    println("Seperator will be added")
                } else {
                    // 安全转换避免强制转换崩溃
                    val hashMap = snap.value as? HashMap<String, String> ?: continue
                    val itemName = snap.key
                    val itemCost = hashMap["itemCost"]?.toInt() ?: 0
                    val ItemHelper = Item()
                    itemName?.let { ItemHelper.setItemName(it) }
                    ItemHelper.setItemCost(itemCost)
                    listItemArrayList.add(ItemHelper)
                }
                count++
            }
            listAdapter!!.notifyDataSetChanged()
        }
    })
}

三、其他代码优化建议

  1. 空安全增强:避免强制非空导致崩溃
// 原用户手机号获取
user = mAuth!!.currentUser!!.phoneNumber as String
// 改成安全处理
user = mAuth?.currentUser?.phoneNumber ?: run {
    // 用户未登录时跳转到登录页
    startActivity(Intent(this, LoginActivity::class.java))
    finish()
    return@onCreate
}
  1. 移除监听避免内存泄漏
override fun onStart() {
    super.onStart()
    mAuth?.addAuthStateListener(mAuthListener!!)
}

override fun onDestroy() {
    super.onDestroy()
    mAuth?.removeAuthStateListener(mAuthListener!!)
    // 如果需要移除数据库监听,可将menucatRef保存为成员变量,在这里调用removeEventListener
}
  1. ListView性能优化
    将inflate的第三个参数从null改为parent, false,利用父布局的LayoutParams提升性能:
// 原分隔符布局加载
customView = layoutInflater.inflate(R.layout.seperator, null)
// 改成
customView = layoutInflater.inflate(R.layout.seperator, parent, false)

// 列表项同理
customView = layoutInflater.inflate(R.layout.list_item, parent, false)

内容的提问来源于stack exchange,提问作者Anurag Joshi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:04:41