求助:Firestore数据无法在RecyclerView实时搜索结果中显示
Firestore结合RecyclerView实时搜索无数据显示问题修复
我在Android应用中尝试基于Firestore数据库实现RecyclerView实时搜索功能,但运行代码后搜索结果无任何数据显示,推测问题出在MainActivity.kt和Adapter.kt中,需要修正代码以正常展示Firestore数据。
现有代码
MainActivity.kt
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding val db = Firebase.firestore val firebaseFirestore : FirebaseFirestore = FirebaseFirestore.getInstance() private var searchList: List<dataClass> = ArrayList() private val adapter = adapter(searchList) override fun onCreate(savedInstanceState: Bundle?) { binding = ActivityMainBinding.inflate(layoutInflater) super.onCreate(savedInstanceState) setContentView(binding.root) supportActionBar?.hide() val search_list :RecyclerView = findViewById(R.id.search_list) search_list.hasFixedSize() search_list.layoutManager = LinearLayoutManager(this) search_list.adapter = adapter binding.searchField.addTextChangedListener(object : TextWatcher{ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { } override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { val search : String = binding.searchField.text.toString() //Search in Firestore searchInFirestore(search.toLowerCase()) } override fun afterTextChanged(p0: Editable?) { } }) } private fun searchInFirestore(search: String) { firebaseFirestore.collection("mechanic").orderBy("city").startAt(search).endAt("$search\uf8ff").get() .addOnCompleteListener { if(it.isSuccessful){ searchList = it.result!!.toObjects(dataClass::class.java) adapter.searchlist =searchList adapter.notifyDataSetChanged() } else{ Log.d(TAG,"Error:${it.exception!!.message}") } } } }
Adapter.kt
class adapter(var searchlist: List<dataClass> ) : RecyclerView.Adapter<adapter.viewholder>(){ inner class viewholder(itemView: View):RecyclerView.ViewHolder(itemView) { val mname: TextView = itemView.findViewById(R.id.name) val mphone : TextView = itemView.findViewById(R.id.phone) val addres: TextView = itemView.findViewById(R.id.address) fun bind(dataClass: dataClass){ mname.text = dataClass.name mphone.text = dataClass.phone addres.text = dataClass.address } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): viewholder { val view = LayoutInflater.from(parent.context).inflate(R.layout.single_search,parent,false) return viewholder(view) } override fun onBindViewHolder(holder: viewholder, position: Int) { holder.bind(searchlist[position]) } override fun getItemCount(): Int { return searchlist.size } }
dataClass.kt
data class dataClass ( val name: String ="",val phone:String = "", val address : String ="" )
布局文件
single_search.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/name" android:layout_width="0dp" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.022" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Phone no" app:layout_constraintBaseline_toBaselineOf="@+id/phone" app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/phone" android:layout_width="144dp" android:layout_height="19dp" android:layout_marginTop="12dp" android:layout_marginBottom="20dp" android:text="TextView" app:layout_constraintBottom_toTopOf="@+id/address" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.088" app:layout_constraintStart_toEndOf="@+id/textView2" app:layout_constraintTop_toBottomOf="@+id/name" /> <TextView android:id="@+id/textView5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Address" app:layout_constraintBaseline_toBaselineOf="@+id/address" app:layout_constraintStart_toStartOf="parent" /> <TextView android:id="@+id/address" android:layout_width="260dp" android:layout_height="65dp" android:layout_marginTop="86dp" android:text="TextView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.229" app:layout_constraintStart_toEndOf="@+id/textView5" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/search_field" android:layout_width="0dp" android:layout_height="wrap_content" android:ems="10" android:inputType="textPersonName" android:paddingTop="20sp" android:text="Search here" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:layout_width="408dp" android:layout_height="642dp" android:id="@+id/search_list" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/search_field" /> </androidx.constraintlayout.widget.ConstraintLayout>
问题分析与修复方案
核心问题点
- Firestore字段映射不匹配:查询的
city字段未在dataClass中定义,导致数据无法正确映射 - Adapter数据更新逻辑缺陷:直接修改集合变量未保证Adapter感知到数据变化
- Firestore索引缺失:
orderBy+startAt/endAt查询需要提前创建复合索引 - 布局高度错误:列表项根布局为
match_parent,导致单一项占满屏幕,视觉上无数据
修复后的代码
修改后的dataClass.kt(添加city字段)
data class dataClass( val name: String = "", val phone: String = "", val address: String = "", val city: String = "" // 新增Firestore对应的city字段 )
修改后的Adapter.kt(规范命名+添加数据更新方法)
// 类名首字母大写,符合Kotlin命名规范 class SearchAdapter(var searchList: List<dataClass>) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val mName: TextView = itemView.findViewById(R.id.name) val mPhone: TextView = itemView.findViewById(R.id.phone) val mAddress: TextView = itemView.findViewById(R.id.address) fun bind(data: dataClass) { mName.text = data.name mPhone.text = data.phone mAddress.text = data.address } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.single_search, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(searchList[position]) } override fun getItemCount(): Int { return searchList.size } // 统一数据更新入口,确保Adapter正确感知数据变化 fun updateData(newList: List<dataClass>) { searchList = newList notifyDataSetChanged() } }
修改后的MainActivity.kt(优化查询逻辑+Adapter初始化)
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val firebaseFirestore: FirebaseFirestore = FirebaseFirestore.getInstance() private var searchList: List<dataClass> = ArrayList() private lateinit var adapter: SearchAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) supportActionBar?.hide() val searchListRv: RecyclerView = findViewById(R.id.search_list) searchListRv.setHasFixedSize(true) searchListRv.layoutManager = LinearLayoutManager(this) // 延迟初始化Adapter,避免空数据问题 adapter = SearchAdapter(searchList) searchListRv.adapter = adapter binding.searchField.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { val searchQuery = binding.searchField.text.toString().trim() if (searchQuery.isNotEmpty()) { searchInFirestore(searchQuery.toLowerCase()) } else { // 清空搜索结果 adapter.updateData(emptyList()) } } override fun afterTextChanged(p0: Editable?) {} }) } private fun searchInFirestore(search: String) { // 若需搜索name字段,将orderBy("city")改为orderBy("name") firebaseFirestore.collection("mechanic") .orderBy("city") .startAt(search) .endAt("$search\uf8ff") .get() .addOnCompleteListener { task -> if (task.isSuccessful) { val resultList = task.result?.toObjects(dataClass::class.java) ?: emptyList() adapter.updateData(resultList) } else { Log.e("MainActivity", "查询失败: ${task.exception?.message}") } } } }
修改后的single_search.xml(修复布局高度+优化排版)
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" <!-- 修改为wrap_content,避免单一项占满屏幕 --> android:padding="16dp"> <!-- 添加内边距优化显示效果 --> <TextView android:id="@+id/name" android:layout_width="0dp" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" android:text="TextView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Phone no:" app:layout_constraintBaseline_toBaselineOf="@+id/phone" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/name" /> <TextView android:id="@+id/phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:text="TextView" app:layout_constraintStart_toEndOf="@+id/textView2" app:layout_constraintTop_toBottomOf="@+id/name" /> <TextView android:id="@+id/textView5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Address:" app:layout_constraintBaseline_toBaselineOf="@+id/address" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/phone" /> <TextView android:id="@+id/address" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:text="TextView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/textView5" app:layout_constraintTop_toBottomOf="@+id/phone" /> </androidx.constraintlayout.widget.ConstraintLayout>
最后必要操作
- 创建Firestore索引:登录Firestore控制台,为
mechanic集合创建city字段的升序索引(若搜索name则创建name字段索引) - 配置安全规则:开发阶段可暂时设置规则为
allow read, write: if true;,上线后需修改为符合业务需求的权限规则
内容的提问来源于stack exchange,提问作者Vedant Khandelwal
相关产品推荐
相关产品推荐

