如何在Android中实现Firebase数据库的自定义搜索和过滤

420 阅读7分钟

在Android中实现Firebase数据库的自定义搜索和过滤

Firebase实时数据库是一个NoSQL数据库,它允许我们在用户之间实时存储和同步数据。这是一个大型的JSON对象,开发者可以使用单个API进行实时管理。

查询指令简化了对MySQL和SQLite等关系型数据库中不同属性数据的过滤和搜索。然而,在Firebase实时数据库中,即使进行简单的数据查询,过滤和搜索也会相对复杂。

前提条件

要跟上本教程,你应该。

  • 对Kotlin编程语言有一个基本的掌握。
  • 理解Android开发的基本原理。
  • 熟悉如何将你的项目连接到Firebase。

目标

本文打算指导和帮助读者充分理解Firebase实时数据库中的搜索和过滤,以及使这个过程成功的所有实现和方法。

Firebase实时数据库是如何结构化的

在大多数数据库中,数据都是以表和行来组织的。然而,Firebase实时数据库是一个NoSQL数据库,它以JSON格式组织数据,这些数据被安排在一个叫做集合的树状结构中。

为了创建这种树状结构,被称为节点的元素被两个相连的节点共享的一般路径连接起来。

如下图所示,一个子节点可以嵌套在连接的节点中,形成一个广泛的嵌套结构。

database structure

Firebase实时数据库的优势

  • 能够通过单一路径访问数据库中的数据。
  • 在Firebase中创建数据格式很简单,因为你可以使用一个自动生成的ID或者定义你自己的。
  • 一个单一的子路径列出了存储数据里面的所有变化。
  • 它是一个NoSQL数据库,这意味着它不限于关系型数据库。

Firebase实时数据库的劣势

  • 寻找某个项目是很困难的,因为没有查询语言可以使用。
  • 删除一个特定的子项目很困难,因为它需要在所有的数据中寻找要删除的项目的位置。

在Firebase实时数据库中过滤和搜索数据的方法

尽管在Firebase数据库中搜索数据很困难,但还是有各种技术来搜索和过滤数据。

这些标准是:。

  • 通过使用id ,这通常是用来寻找一个特定的值,该值有其id
  • 通过使用一个子路径,从中列出数据库中数据的任何变化。
  • 通过使用一个可以作为参数的变量。
  • 采用过滤技术,如orderByChild

用于过滤数据的方法

有几种方法有助于对Firebase数据进行过滤和排序。为了进行排序,我们使用了下面的方法。

  • orderByChild - 该方法根据指定的子集合或子路径对数据进行排序。
  • orderByKey - 根据指定的键值执行排序。
  • orderByValue- 根据给定的子值对数据进行排序。

另外,过滤是由以下方法处理的。

  • limitToFirst -从给定的参数值开始过滤数据。
  • limitToLast - 将数据限制在最后提供的值。
  • startAt - 从提供的键或值开始过滤数据。
  • startAfter - 在给定的键或值之后过滤。
  • equalTo 过滤某一个键或值类别的数据。
  • endAt - 将数据限制在提供的键或值上。
  • endBefore - 准确地在指定的值之前过滤数据。

让我们开始吧 :)

第1步:创建一个空项目

启动Android Studio并创建一个空的活动项目,从头开始一个新项目。接下来,给你的项目一个描述性的名字。

project

第2步:将你的项目链接到Firebase

为了获得对Firebase数据库的访问,你的项目必须连接到Firebase,这可以通过以下方式完成。

在你的浏览器上,搜索Firebase控制台,进入Add project -> 输入项目名称 -> 选择Firebase的默认账户 -> 创建项目。

确保你已经启用了Realtime数据库。

第3步:设计用户界面

在这一步,我们将创建一个简单的布局,这将有助于以列表的方式显示数据。例如,我们将有一个EditText ,以输入一个搜索词。另外,该布局将有两个按钮:一个用于过滤数据,另一个用于搜索。

注意:你必须创建一个行的布局,映射出你的数据将如何出现在RecyclerView中。

用户界面应该如下图所示。

user_interface

第4步:创建模型类和适配器类

模型类是用来映射Firebase中的数据,并为这些数据建立一个访问点。下面的代码演示了如何创建一个模型类。

data class Students(
    val id:String? = "",
    val name: String? = "",
    val regno: String? = "",
    val gender:String? = "",
    val amount: Int? = 0,
    val age:Int?= 0
)

适配器类作为访问数据和RecyclerViews之间的链接。下面的代码演示了如何创建一个适配器类。

class StudentsAdapter: ListAdapter<Students, StudentsAdapter.MyHolder>(COMPARATOR) {

// Calculates the difference between the available data and new data
    private object COMPARATOR: DiffUtil.ItemCallback<Students>(){
        override fun areItemsTheSame(oldItem: Students, newItem: Students): Boolean {
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: Students, newItem: Students): Boolean {
            return oldItem.id == newItem.id
        }
    }
// An inner class that maps data with the available views
    inner class MyHolder(private val binding: StudentsRowBinding): RecyclerView.ViewHolder(binding.root) {
        fun bind(student: Students?) {
            binding.tvName.text = student?.name
            binding.tvRegNumber.text = student?.regno
            binding.tvAmount.text = student?.amount.toString()
            binding.tvAge.text = student?.age.toString()
            binding.tvGender.text = student?.gender.toString()
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
        return MyHolder(StudentsRowBinding.inflate(LayoutInflater.from(parent.context),parent,false))
    }

    override fun onBindViewHolder(holder: MyHolder, position: Int) {
        val student = getItem(position)
        holder.bind(student)
    }
}

第5步:显示Firebase的数据

在执行任何搜索或过滤操作之前,首先显示来自Firebase的数据是至关重要的。

首先,创建一个DatabaseReference 的实例。

private lateinit var databaseReference: DatabaseReference

在你的onCreate ,确保你已经初始化了databaseReference

databaseReference = FirebaseDatabase.getInstance().getReference("students")

定义以下方法并在你的onCreate 方法中调用它来获取数据。

// fetching all values from firebase
private fun getStudents() {
    databaseReference.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                if (snapshot.exists()) {
                    for (i in snapshot.children) {
                        val student = i.getValue(Students::class.java)
                        if (student != null) {
                            studentsList.add(student)
                        }
                    }
                    studentAdapter.submitList(studentsList)
                    binding.recyclerStudents.adapter = studentAdapter
                } else {
                    Toast.makeText(applicationContext, "Data Does not Exist", Toast.LENGTH_SHORT).show()
                }
            }

            override fun onCancelled(error: DatabaseError) {

            }
        })
}

第6步:实现过滤

过滤可以通过两种方式完成。

  • 传递过滤参数或
  • 使用数据中的一个普通值,比如说性别。

要使用一个标准的类别进行过滤,我们可以定义这样一个函数。

// filtering in general
private fun filterGender(){
    // Specifying path and filter category and adding a listener
    databaseReference.orderByChild("gender").equalTo("female").addValueEventListener(object:ValueEventListener{
        override fun onDataChange(snapshot: DataSnapshot) {
            if(snapshot.exists()){
                studentsList.clear()
                for (i in snapshot.children){
                    val female = i.getValue(Students::class.java)
                    studentsList.add(female!!)
                }
                studentAdapter.submitList(studentsList)
                binding.recyclerStudents.adapter = studentAdapter
            } else{
                Toast.makeText(applicationContext, "Data is not found", Toast.LENGTH_SHORT).show()
            }
        }

        override fun onCancelled(error: DatabaseError) {

        }
    })
}

要使用RecyclerView中显示的列表中的一个特定参数进行过滤。我们可以通过首先定义一个名为filter 的方法进行搜索。

private  fun filter(e: String) {
    //Declare the array list that holds the filtered values
    val filteredItem = ArrayList<Students>()
    // loop through the array list to obtain the required value
    for (item in studentsList) {
        if (item.name!!.toLowerCase().contains(e.toLowerCase())) {
            filteredItem.add(item)
        }
    }
    // add the filtered value to adapter
    studentAdapter.submitList(filteredItem)
}

之后,我们可以在一个TextWatcher 里面调用这个函数,这个函数会监听在EditText 中输入的每一个字符。然后会在列表中循环,看是否可以找到输入的字符。

binding.etSearch.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) {

            }

            override fun afterTextChanged(editable: Editable?) {
                filter(editable.toString())
            }
        })

第7步:实现搜索

人们可以根据某个值来搜索数据,比如说名字。这可以通过定义一个方法来实现,该方法接收搜索条件并从Firebase中执行搜索。

private fun searchByName(name: String) {
    // adding a value listener to database reference to perform search
    databaseReference.addValueEventListener(object:ValueEventListener{
        override fun onDataChange(snapshot: DataSnapshot) {
            // Checking if the value exists
            if (snapshot.exists()){
                studentsList.clear()
                // looping through the values
                for (i in snapshot.children){
                    val student = i.getValue(Students::class.java)
                    // checking if the name searched is available and adding it to the array list
                    if (student!!.name == name){
                        studentsList.add(student)
                    }
                }
                //setting data to RecyclerView
                studentAdapter.submitList(studentsList)
                binding.recyclerStudents.adapter = studentAdapter
            } else{
                Toast.makeText(applicationContext, "Data does not exist", Toast.LENGTH_SHORT).show()
            }
        }

        override fun onCancelled(error: DatabaseError) {
        }
    })
}

当你运行这个应用程序时,你应该得到以下输出。

demo

结论

在本教程中,我们已经讨论了Firebase实时数据库是如何构建其数据的,我们执行搜索和过滤的不同方式,过滤数据时使用的方法,以及最后在Android中实现搜索和过滤。