When it comes to filtering, the interface on the back end has to upload a field and return the corresponding data based on that field. The next time you connect to someone else, the interface returns all the data directly and filters it. I… I said is not a SQL statement, how convenient to change the interface, I tried to persuade, and then was reported back, cut, is not screening, it is better to ask for their own.

1. The rendering

2. The train of thought

Since it is screening, it is not without comparison. There is no better way to do this than to loop through comparisons and then update the adapter. Page refresh. But filter call to be convenient, how to compare just convenient we call? If you happen to see a Filterable, make the Adapter inherit from that interface and implement getFilter(), which implements the specific filtering logic.

3. Implementation steps

3.1 Data Bean classes

class MyBean(var type:String,var name:String,var deliverType:String)
Copy the code

Here we simply create a data Bean class, and then our filter fields are filtered by Type and deliverType.

3.2 Creating an Adapter

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

    // Store the original data
    private var mSourceList = mutableListOf<MyBean>()

    // Store filtered data
    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)
    }

    /** * Note: this returns the filtered collection size */
    override fun getItemCount(a): Int {
        return mFilterList.size
    }

    inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        // Product name
        var name: TextView = itemView.findViewById(R.id.tvName)
        // Distribution mode
        var deliverType: TextView = itemView.findViewById(R.id.tvDeliverType)
    }
}
Copy the code

It’s just like any other Adapter we create. But note the following

  • 1. Here we create two collections mSourceList and mFilterList. MFilterList is mainly used to store filtered data, while mSourceList is mainly used to recover filtered data, so that there is no need to request data again.

  • The getItemCount() method returns the size of the filtered collection.

I have a question:

If we do not filter, and because our mFilterList defaults to empty and getItemCount() returns its size 0, do we default to showing no data?

3.3 Inheriting the Filterable Interface

  • 1. After inheriting the Filterable interface, implement its getFilter() method, which requires us to return a Filter object.

  • 2. We overwrite the Filter performFiltering() method and the publishResults() method. PerformFiltering () is used to implement our specific filtering logic, and publishResults() is used to update our filtered data.

  • 3. Because the filter that filters through performFiltering() is a string, and we have two filters, we pass the filter as a Json object so that we can have multiple strings.

  • 4. The specific filtering operation here is to use the filter() method of Collection for filtering

    • Condition1 and condition2 are null condition1 and condition2 are null condition1 and condition2 are null condition1 and condition2 are null condition1 and condition2 are null condition2

    • (2) Otherwise, the filter() method is used to filter according to conditions. Finally, the filtered set is assigned to the value field of the FilterResults() object and returned

  • 5, publishResults (charSequence: charSequence, filterResults: The value field in the FilterResults object in the FilterResults method is the filtered set returned by performFiltering(), where RecyclerView is updated.

See the following code for concrete implementation:

class MyAdapter(data: MutableList<MyBean>) :
    RecyclerView.Adapter<MyAdapter.MyViewHolder>(), Filterable {
        /** * The specific filtering operation is performed */ by default after the adapter is created
    override fun getFilter(a): Filter {
        return object : Filter() {
            // Perform filtering
            override fun performFiltering(charSequence: CharSequence): FilterResults {
                val charString = charSequence.toString()
                Log.i(TAG, "PerformFiltering: Performs filtering operations with the following fields:$charString")

                val jsonObject = JSONObject(charString)
                // Filter condition 1
                var condition1 = jsonObject.getString("condition1")
                // Filter condition 2
                var condition2 = jsonObject.getString("condition2")

                // Store filtered data
                var theFilterList = if (condition1.isEmpty() && condition2.isEmpty()) {
                    // If the content is not filtered, use the source data
                    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
            }

            // Return the filtered value and update it
            override fun publishResults(
                charSequence: CharSequence,
                filterResults: FilterResults
            ) {
                mFilterList = filterResults.values as MutableList<MyBean>
                notifyDataSetChanged()
            }
        }
    }
}
Copy the code

3.4 Filtering Calls

class MainActivity : AppCompatActivity() {
    
    // Filter condition 1
    var condition1 = ""
    // Filter condition 2
    var condition2 = ""
    // Total filter conditions
    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"."Filter Condition one")
        jsonObject.put("condition2"."Filter Condition 2")
        myAdapter.filter.filter(jsonObject.toString())
        
      
    }
}

Copy the code

Myadapter.filter.filter (jsonObject.tostring ())) condition1 and condition2 are null.

See code for details

4. The optimization

In fact, the filtering operation in getFilter() can be optimized

var theFilterList = if (condition1.isEmpty() && condition2.isEmpty()) {
    // If the content is not filtered, use the source data
    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 }
}
Copy the code

Can see the else {} is when condition1 and condition2 under null for screening, but if we use filtered drop-down box, select the first condition has carried on a screen after the condition1, The condition1 isn’t empty condition2 is empty, the else if (condition2. IsEmpty ()) {} in the source data for the selection; When selecting the second condition again, and have made a selection, namely condition1 isn’t empty condition2 is empty, not in the else {} is to filter the source data, in fact, we should be filtered under the results of the first is the optimal solution.

Idea is very good, but difficult to achieve a lot, when two conditions are not empty, we need to decide first sifting down data is based on which the filter, in the two terms are not empty after screening, again, change one of the filter, we need another first selection under the condition of data to the selection, more and more trouble, don’t consider for a while, The trouble that has good plan gives a train of thought.

5. Pay attention to

By default, the size returned by Adapter is the filtered size, but we do not filter by default, so there will be no data, so we need to manually call the filter after setting the Adapter: myAdapter.filter.filter(jsonObject.toString()). I didn’t write it in the project because appspinner will default to item 0 and I call the filter in its onItemSelected() callback.

6. Summary

Generally speaking, it is not difficult to update the data and update the layout of the same set, but the difference is that the Filterable interface is used to implement, making the way to filter calls easier. However, this implementation is more suitable for small or fixed data. If the data volume is large, or the data will be continuously loaded and expanded, using this method will only reduce the efficiency as the data volume increases, which is obviously not suitable.

Github project address

7. Refer to the article

Set filter operation

Google docs