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没什么两样。但要注意以下几点

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

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

有个疑问:

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

3.3 继承Filterable接口

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

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

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

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

    • 当condition1和condition2为空时,返回原数据mSourceList

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

  • 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接口实现,使得筛选调用的方式更简单。但是这种实现更多的是适用于数据量小或者固定的数据,如果数据量大,或者数据会一直上拉加载扩充,使用这种方式只会让效率随着数据量的增大而越来越低,显然不合适,下次后端还强硬不改,那就只能开怼了。

7. 参考文章

集合过滤操作

Google文档

关注公众号,回复 Filterable 获取文章源码
在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现 RecyclerView 左滑显示删除按钮的功能,你可以按照以下步骤进行操作: 1. 首先,在你的 RecyclerView 的 Adapter 中添加一个接口,例如 `OnItemSwipeListener`,用于处理左滑显示删除按钮的事件。 ```java public interface OnItemSwipeListener { void onItemSwiped(int position); } ``` 2. 在你的 RecyclerView 的 ViewHolder 中添加一个滑动删除的手势检测器,并在 `onBindViewHolder` 方法中为每个 ViewHolder 设置这个手势检测器。 ```java public class YourViewHolder extends RecyclerView.ViewHolder { private GestureDetectorCompat gestureDetector; private View deleteButton; // 删除按钮 public YourViewHolder(View itemView, final OnItemSwipeListener listener) { super(itemView); deleteButton = itemView.findViewById(R.id.delete_button); // 初始化删除按钮 gestureDetector = new GestureDetectorCompat(itemView.getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // 判断是向左滑动 if (distanceX < 0 && Math.abs(distanceX) > Math.abs(distanceY)) { deleteButton.setVisibility(View.VISIBLE); // 显示删除按钮 return true; } return false; } }); } public void bindData() { // 绑定数据到 ViewHolder deleteButton.setVisibility(View.GONE); // 隐藏删除按钮 } public boolean onTouchEvent(MotionEvent event) { return gestureDetector.onTouchEvent(event); } } ``` 3. 在你的 RecyclerView 的 Adapter 中实现 `OnItemSwipeListener` 接口,并在对应的方法中处理左滑显示删除按钮的事件。 ```java public class YourAdapter extends RecyclerView.Adapter<YourViewHolder> implements OnItemSwipeListener { // 其他代码 @Override public void onItemSwiped(int position) { // 处理左滑显示删除按钮事件,可以在这里更新对应位置的 ViewHolder 的 UI // 例如,你可以调用 notifyItemChanged(position) 更新 UI } } ``` 通过以上步骤,你就可以实现 RecyclerView 左滑显示删除按钮的功能了。当用户向左滑动某个 ViewHolder 时,会触发滑动事件,你可以在相应的方法中处理显示删除按钮的逻辑并更新 UI。当用户滑动回原始位置时,你可以隐藏删除按钮。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重拾丢却的梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值