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

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

1. 效果图

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文档

到此这篇关于Android Filterable实现Recyclerview筛选功能的示例代码的文章就介绍到这了,更多相关Android Recyclerview筛选内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • RecyclerView+SnapHelper实现无限循环筛选控件

    项目用到横向滑动的单项选择控件.需求如下: 1.选项由后台配置,也就是控件要动态设置宽度:2.每次滑动都要左对齐,并默认选中最左边选项:3.控件可以无限循环. 动态设置控件宽度:获取adapter的item宽度:获取adapter里装载的itemCount. 每次滑动都要左对齐:利用LinearSnapHelper中calculateDistanceToFinalSnap的方法实现: 默认选中最左item:利用RecycleView的LayoutManager中查找显示第一项的方法linearM

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

    原先碰到筛选这种功能时,后端的接口都会让上传一个字段,根据字段来返回相应的数据.后来一次和别人对接时,接口直接返回全部数据,而且还要实现筛选功能.我...我说不就是一条sql语句的事,改接口多方便,我苦心劝导,然后被怼回来,切,不就是筛选嘛,求人不如自己搞. 1. 效果图 2. 思路 既然是筛选,那就少不了比较.也没有什么好的办法,无非就是循环对比,然后将适配器进行数据更新.页面刷新即可.但筛选的调用要方便,怎么比较才方便我们调用呢?偶然间看到了Filterable,使Adapter继承自该接口

  • Android 使用 RxJava2 实现倒计时功能的示例代码

    倒计时功能被广泛运用在 App 启动页.短信验证码倒计时等,通常做法是起一个Handler ,在子线程里完成倒计时,如今这一做法有了替代品 -- RxJava ,RxJava是被行内一致认可的第三方开源库,我们可以使用RxJava实现倒计时功能. 示例图: 示例代码: 导入必要的库文件(Android支持库和Reactivex系列支持库) implementation 'com.android.support:appcompat-v7:27.1.0' implementation 'com.an

  • android系统分享的自定义功能的示例代码

    分享功能是app中特别常见的功能,国内的app基本都支持分享到微信 QQ等主流的社交应用.至于分享功能的实现大多是使用第三方的share sdk一步到位,或者分享的app比较少比如就一个微信 那通常使用微信sdk的分享模块即可.但其实android系统就给我们提供过一种分享的实现方式,代码也比较简单如下 Intent share = new Intent(Intent.ACTION_SEND); share.setType("text/plain"); share.putExtra(I

  • Android首页无限轮播功能的示例代码

    最近工作不是很忙,也跟大神学习下总结一些小的技术点: 对于一个App几乎都有Banner广告功能,也就是我们常见的轮播图,当然目前市场第三方框架已经非常成熟了,尤其是youth5201314/banner这里有github地址也可以学习下:https://github.com/youth5201314/banner.git 那么下面给大家介绍我的一些总结: 首先分析下轮播图的设计 多张轮播图定时效果 指示点以及每张图片的文字说明 实现无限轮播,可滑动,图片点击事件 开始布局: <Relative

  • element-ui组件table实现自定义筛选功能的示例代码

    element-ui默认的table组件支持的表头筛选(过滤)是比较简单的,只支持数组的方式,单选或多选的形式,但有时候我们喜欢支持输入框形式(其实感觉有点扯淡,一般列表页上面都有搜索条件). 注意:里面用到的jsx语法,可能需要安装一些插件. 准备工作: 1.安装babel-plugin-jsx-v-model插件 npm i babel-plugin-jsx-v-model -D 或者 yarn add babel-plugin-jsx-v-model -D 2..babelrc: { "p

  • Android仿微信发送语音消息的功能及示例代码

    微信的发送语音是有一个向上取消的,我们使用onTouchListener来监听手势,然后做出相应的操作就行了. 直接上代码: //语音操作对象 private MediaPlayer mPlayer = null; private MediaRecorder mRecorder = null; //语音文件保存路径 private String FileName = null; FileName = Environment.getExternalStorageDirectory().getAbs

  • Android 自定义LineLayout实现满屏任意拖动功能的示例代码

    1.前言 在开发中,会有需求实现控件在屏幕随意拖动,这就需要自定义View,然后在OnTouchEvent事件中,处理MotionEvent.ACTION_MOVE事件,然后通过坐标点传值给onlayout方法,来实现控件的任意拖动,具体代码如下: import android.content.Context; import android.util.AttributeSet; import android.view.Display; import android.view.MotionEven

  • Android实现强制下线功能的示例代码

    一.回顾 上次连载写了两个类,一个类ActivityCollector.java用于管理所有的活动:一个类是BaseActivity.java作为所有活动的父类: 还有一个放在layout目录中的登录界面login.xml 二.登录页面的活动 接下来写一个登录页面的活动,继承自BaseActivity.java package com.example.broadcastbestpractice; import android.content.Intent; import android.os.B

  • Android实现显示和隐藏密码功能的示例代码

    在前端中我们知道用javascript就可以可以很容易实现,那么在Android中怎么实现这个功能呢? Java代码 package com.example.test2; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.text.method.HideReturnsTransformationMethod; import android.text.method.Pa

  • Android ItemDecoration 实现分组索引列表的示例代码

    本文介绍了Android ItemDecoration 实现分组索引列表的示例代码,分享给大家.具体如下: 先来看看效果: 我们要实现的效果主要涉及三个部分: 分组 GroupHeader 分割线 SideBar 前两个部分涉及到一个ItemDecoration类,也是我们接下来的重点,该类是RecyclerView的一个抽象静态内部类,主要作用就是给RecyclerView的ItemView绘制额外的装饰效果,例如给RecyclerView添加分割线. 使用ItemDecoration时需要继

随机推荐