Android Filterable实现Recyclerview筛选功能的示例代码

作者:重拾丢却的梦 时间:2023-08-30 16:52:47 

目录
  • 1. 效果图

  • 2. 思路

  • 3. 实现步骤

    • 3.1 数据Bean类

    • 3.2 创建适配器

    • 3.3 继承Filterable接口

    • 3.4 过滤调用

  • 4. 优化

    • 5. 注意

      • 6. 总结

        • 7. 参考文章

          原先碰到筛选这种功能时,后端的接口都会让上传一个字段,根据字段来返回相应的数据。后来一次和别人对接时,接口直接返回全部数据,而且还要实现筛选功能。我...我说不就是一条sql语句的事,改接口多方便,我苦心劝导,然后被怼回来,切,不就是筛选嘛,求人不如自己搞。

          1. 效果图

          Android Filterable实现Recyclerview筛选功能的示例代码

          2. 思路

          既然是筛选,那就少不了比较。也没有什么好的办法,无非就是循环对比,然后将适配器进行数据更新。页面刷新即可。但筛选的调用要方便,怎么比较才方便我们调用呢?偶然间看到了Filterable,使Adapter继承自该接口,实现getFilter()方法,在该方法里实现具体的过滤逻辑即可。

          3. 实现步骤

          3.1 数据Bean类


          class MyBean(var type:String,var name:String,var deliverType:String)

          这里我们简单的创建个数据Bean类,后面我们的筛选字段是根据type和deliverType来进行筛选。

          3.2 创建适配器


          class MyAdapter(data: MutableList<MyBean>) :
          RecyclerView.Adapter<MyAdapter.MyViewHolder>(), Filterable {

          //存放原数据
          private var mSourceList = mutableListOf<MyBean>()

          //存放过滤后的数据
          private var mFilterList = mutableListOf<MyBean>()

          init {
           mSourceList = data
          }

          override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
           holder.name.text = mFilterList[position].name
           holder.deliverType.text = mFilterList[position].deliverType
          }

          override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
           var view =
            LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
           return MyViewHolder(view)
          }

          /**
           * 注意:这里返回过滤后的集合大小
           */
          override fun getItemCount(): Int {
           return mFilterList.size
          }

          inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
           //商品名称
           var name: TextView = itemView.findViewById(R.id.tvName)
           //配送方式
           var deliverType: TextView = itemView.findViewById(R.id.tvDeliverType)
          }
          }

          和我们平时创建的Adapter没什么两样。但要注意以下几点

          1、这里我们创建了两个集合mSourceList和mFilterList,mFilterList主要是用来存放过滤后的数据,而mSourceList主要是用来在筛选后数据恢复时使用,使得不用再去请求一次数据。

          2、getItemCount()方法返回过滤后的集合的大小。

          有个疑问:
          假如我们没有进行过滤,而因为我们的mFilterList默认为空,且getItemCount()返回的是它的大小0,那我们默认是不是就显示不出数据?

          3.3 继承Filterable接口

          1、继承Filterable接口后,实现其getFilter()方法,该方法需要我们返回一个Filter过滤器对象。

          2、我们重写Filter的performFiltering()方法和publishResults()方法,performFiltering()用来实现我们具体过滤的逻辑操作,publishResults()用来将我们过滤后的数据进行更新。

          3、因为performFiltering()传来的过滤条件是一段字符串,而我们的过滤条件有两个,所以我们将过滤的条件转化为Json对象传过来,那样就可以得到多个过滤条件的字符串了。

          4、这里我们的具体过滤操作是使用Collection的filter()方法进行过滤

          (1)当condition1和condition2为空时,返回原数据mSourceList

          (2)否则使用filter()方法按条件进行过滤,最后将过滤后的集合赋值给FilterResults()对象的value字段,并将其返回

          5、publishResults(charSequence: CharSequence,filterResults: FilterResults)方法中filterResults对象内的value字段是我们performFiltering()方法返回的过滤后的集合,在这里我们将RecyclerView进行更新。

          具体实现见以下代码:


          class MyAdapter(data: MutableList<MyBean>) :
          RecyclerView.Adapter<MyAdapter.MyViewHolder>(), Filterable {
           /**
           * 具体的执行过滤的操作
           * 创建适配器后会默认的执行一次
           */
          override fun getFilter(): Filter {
           return object : Filter() {
            //执行过滤操作
            override fun performFiltering(charSequence: CharSequence): FilterResults {
             val charString = charSequence.toString()
             Log.i(TAG, "performFiltering: 执行过滤操作,过滤字段为:$charString")

          val jsonObject = JSONObject(charString)
             //筛选条件一
             var condition1 = jsonObject.getString("condition1")
             //筛选条件二
             var condition2 = jsonObject.getString("condition2")

          //存放已过滤的数据
             var theFilterList = if (condition1.isEmpty() && condition2.isEmpty()) {
              //没有过滤的内容,则使用源数据
              mSourceList
             } else if (condition2.isEmpty()) {
              mSourceList.filter { it.type == condition1 }
             } else if (condition1.isEmpty()) {
              mSourceList.filter { it.deliverType == condition2 }
             } else {
              mSourceList.filter { it.type == condition1 && it.deliverType == condition2 }
             }
             val filterResults = FilterResults()
             filterResults.values = theFilterList
             return filterResults
            }

          //把过滤后的值返回出来并进行更新
            override fun publishResults(
             charSequence: CharSequence,
             filterResults: FilterResults
            ) {
             mFilterList = filterResults.values as MutableList<MyBean>
             notifyDataSetChanged()
            }
           }
          }
          }

          3.4 过滤调用


          class MainActivity : AppCompatActivity() {

          //过滤条件1
          var condition1 = ""
          //过滤条件2
          var condition2 = ""
          //总的过滤条件
          var jsonObject = JSONObject()
          private var dataList = mutableListOf<MyBean>()
          var myAdapter = MyAdapter(dataList)

          override fun onCreate(savedInstanceState: Bundle?) {
           super.onCreate(savedInstanceState)
           setContentView(R.layout.activity_main)

          mRecyclerView.layoutManager = LinearLayoutManager(this)
           mRecyclerView.adapter = myAdapter

          jsonObject.put("condition1","过滤条件一")
           jsonObject.put("condition2","过滤条件二")
           myAdapter.filter.filter(jsonObject.toString())  
          }
          }

          如果想恢复数据不筛选,直接将jsonObject对象内的condition1和condition2字段设为空,然后调用myAdapter.filter.filter(jsonObject.toString())即可。

          具体见代码

          4. 优化

          其实我们getFilter()内的过滤操作还可以优化下


          var theFilterList = if (condition1.isEmpty() && condition2.isEmpty()) {
          //没有过滤的内容,则使用源数据
          mSourceList
          } else if (condition2.isEmpty()) {
          mSourceList.filter { it.type == condition1 }
          } else if (condition1.isEmpty()) {
          mSourceList.filter { it.deliverType == condition2 }
          } else {
          mSourceList.filter { it.type == condition1 && it.deliverType == condition2 }
          }

          可以看到else{}下是当condition1和condition2都不为空的情况下进行的筛选,但是如果我们使用下拉框进行筛选时,选择第一个条件condition1后就已经进行了一次筛选,即condition1不为空condition2为空,在 else if (condition2.isEmpty()){} 里对源数据进行了筛选;再选择第二个条件时,又进行了一次筛选,即condition1不为空condition2不为空,在else{}里又是对源数据进行筛选,其实我们应该是在第一次的结果下进行筛选是最优的办法。

          想法很好,但实现起来困难挺多,在两个条件都不为空时,我们需要判断第一次删选下来的数据是以哪个筛选条件为依据的,在两个条件都不为空筛选后,再次更改其中一个筛选条件,我们需要先将另外一个筛选条件下的数据给筛选出来,越来越麻烦,暂时不考虑了,有好的方案的麻烦给个思路。

          5. 注意

          因为Adapter默认返回的大小是筛选后的尺寸,而我们默认是没有筛选的,导致上来会没有数据,所以我们需要设置适配器后,人为的调用一下筛选才好:myAdapter.filter.filter(jsonObject.toString())。而我在项目中没有写因为AppCompatSpinner会默认的选择第0项,我在其onItemSelected()回调里调用了筛选功能。

          6. 总结

          总的来说并不难,还是更新数据更新布局的那一套,不同的是用了Filterable接口实现,使得筛选调用的方式更简单。但是这种实现更多的是适用于数据量小或者固定的数据,如果数据量大,或者数据会一直上拉加载扩充,使用这种方式只会让效率随着数据量的增大而越来越低,显然不合适,下次后端还强硬不改,那就只能开怼了。
          Github项目地址 https://github.com/myfittinglife/RecyclerViewFilterable

          7. 参考文章

          集合过滤操作
          Google文档

          来源:https://juejin.cn/post/6924132314192543757

          标签:Android,Recyclerview,筛选
          0
          投稿

          猜你喜欢

        • C#实现文件压缩与解压的方法示例【ZIP格式】

          2021-12-31 23:25:20
        • Java毕业设计实战之在线蛋糕销售商城的实现

          2022-06-06 14:25:39
        • Java枚举类型enum的详解及使用

          2023-08-02 14:23:57
        • Spring Boot教程之利用ActiveMQ实现延迟消息

          2023-11-23 18:25:09
        • mybatis原理概述入门教程

          2023-10-08 13:10:57
        • Android之日期及时间选择对话框用法实例分析

          2023-06-26 23:59:34
        • SpringMVC配置多个properties文件之通配符解析

          2021-10-18 02:19:02
        • 在WPF中合并两个ObservableCollection集合

          2022-11-01 18:42:10
        • Android中导航组件Navigation的实现原理

          2022-08-25 13:11:47
        • Java容器类源码详解 Deque与ArrayDeque

          2021-10-15 12:43:21
        • c#如何使用 XML 文档功能

          2023-12-25 03:44:33
        • Android Studio实现帧动画

          2023-11-19 01:28:13
        • C#根据年月日计算星期几的函数

          2022-03-17 04:50:50
        • spring boot学习笔记之操作ActiveMQ指南

          2023-09-12 20:11:52
        • C++ 反汇编之关于Switch语句的优化措施

          2023-09-10 15:00:15
        • springboot 整合 sa-token简介及入门教程

          2023-03-24 01:10:45
        • Java 基础语法

          2021-09-21 19:26:32
        • Java SpringBoot详解集成以及配置Swagger流程

          2023-10-06 03:10:12
        • Android入门之RelativeLayout、FrameLayout用法分析

          2021-10-16 03:21:32
        • java控制台实现学生信息管理系统

          2023-11-29 02:05:20
        • asp之家 软件编程 m.aspxhome.com