Firebase填充ListView出现大量重复项求助及代码优化建议
解决Firebase填充ListView时的重复项问题及代码优化
嘿,别着急!我帮你梳理下问题根源,一步步解决这个重复项的困扰:
一、重复项的核心原因:Adapter中的orig集合错误填充
你在CustomAdapter的getView方法里,每次绘制列表项都会执行orig.addAll(listItem)——这意味着列表每滚动一次,orig就会被重复填充一遍相同的数据。随着滚动次数增加,orig里的重复数据会越来越多,搜索时自然会生成大量重复项。
修复步骤:
- 将
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代码保持不变 } // ... 其他方法不变 }
- 完善搜索过滤逻辑:当前
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数据获取的优化问题
- 避免重复清空和数据覆盖:
当前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() } }) }
三、其他代码优化建议
- 空安全增强:避免强制非空导致崩溃
// 原用户手机号获取 user = mAuth!!.currentUser!!.phoneNumber as String // 改成安全处理 user = mAuth?.currentUser?.phoneNumber ?: run { // 用户未登录时跳转到登录页 startActivity(Intent(this, LoginActivity::class.java)) finish() return@onCreate }
- 移除监听避免内存泄漏
override fun onStart() { super.onStart() mAuth?.addAuthStateListener(mAuthListener!!) } override fun onDestroy() { super.onDestroy() mAuth?.removeAuthStateListener(mAuthListener!!) // 如果需要移除数据库监听,可将menucatRef保存为成员变量,在这里调用removeEventListener }
- 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
相关产品推荐
相关产品推荐

