androidkotlinandroid-recyclerviewsearchview

Filter searchView from RecyclerView with Adapter


Adapter class

class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) :  RecyclerView.Adapter<AppListAdapter.AppViewHolder>() {

    public val appList = arrayListOf<ApplicationInfo>()
    private val checkedAppList = arrayListOf<Boolean>()
    private val packageManager: PackageManager = context.packageManager

    init {
        context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA).sortedBy { it.loadLabel(packageManager).toString() }.forEach { info ->
            if (info.packageName != context.packageName) {
                if (info.flags and ApplicationInfo.FLAG_SYSTEM == 0) {
                    appList.add(info)
                    checkedAppList.add(initialChecked.contains(info.packageName))
                }
            }
        }
    }

    inner class AppViewHolder(private val item: ItemAppBinding) : RecyclerView.ViewHolder(item.root) {
        fun bind(data: ApplicationInfo, position: Int) {

            item.txApp.text = data.loadLabel(packageManager)
            item.imgIcon.setImageDrawable(data.loadIcon(packageManager))
            item.cbApp.isChecked = checkedAppList[position]
            item.cbApp.setOnCheckedChangeListener { _, checked ->
                checkedAppList[position] = checked
            }
        }
    }

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

    override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
        holder.bind(appList[position], position)
    }

    override fun getItemCount(): Int {
        return appList.size
    }

on MainActivity

binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{

    override fun onQueryTextSubmit(query: String?): Boolean {
        binding.searchView2.clearFocus()
       // how to write code filtered by query?
        return false
    }

    override fun onQueryTextChange(newText: String?): Boolean {
        // how to write code filtered by newText?
        return false
    }

})

Solution

  • I believe you want to filter items with "ApplicationInfo.txApp", so i will write the code for it.
    First you need your adapter class to extend Filterable like below, and add one more list to hold all items:

    class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) :  RecyclerView.Adapter<AppListAdapter.AppViewHolder>(), Filterable {
    public val appList = arrayListOf<ApplicationInfo>()
    public val appListFull = ArrayList<ApplicationInfo>(appList)
    // This full list because of when you delete all the typing to searchView
    // so it will get back to that full form.
    

    Then override it's function and write your own filter to work, paste this code to your adapter:

    override fun getFilter(): Filter {
            return exampleFilter
        }
    
        private val exampleFilter = object : Filter() {
            override fun performFiltering(constraint: CharSequence?): FilterResults? {
                val filteredList: ArrayList<ApplicationInfo> = ArrayList()
                if (constraint == null || constraint.isEmpty()) {
                    // when searchview is empty
                    filteredList.addAll(appListFull)
                } else {
                    // when you type something
                    // it also uses Locale in case capital letters different.
                    val filterPattern = constraint.toString().lowercase(Locale.getDefault()).trim()
                    for (item in appListFull) {
                        val txApp = item.txApp
                        if (txApp.lowercase(Locale.getDefault()).contains(filterPattern)) {
                            filteredList.add(item)
                        }
                    }
                }
                val results = FilterResults()
                results.values = filteredList
                return results
            }
    
            @SuppressLint("NotifyDataSetChanged") @Suppress("UNCHECKED_CAST")
            override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                appList.clear()
                appList.addAll(results!!.values as ArrayList<ApplicationInfo>)
                notifyDataSetChanged()
            }
        }
    

    And finally call this filter method in your searchview:

    binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
    
        override fun onQueryTextSubmit(query: String?): Boolean {
            yourAdapter.filter.filter(query)
            yourAdapter.notifyDataSetChanged()
            binding.searchView2.clearFocus()
            return false
        }
    
        override fun onQueryTextChange(newText: String?): Boolean {
            yourAdapter.filter.filter(newText)
            yourAdapter.notifyDataSetChanged()
            return false
        }
    
    })
    

    These should work i'm using something similar to that, if not let me know with the problem.