Ravers
Ravers

Reputation: 1040

Custom ArrayAdapter with filter is not filtering correctly

I have the following ArrayAdapter:

class SearchAdapter(private val activity: Activity, private var species: ArrayList<Specie>) : ArrayAdapter<Specie>(activity, R.layout.specie_item, species) {

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        return convertView ?: createView(position, parent)
    }

    private fun createView(position: Int, parent: ViewGroup?): View {
        val specie = species[position]

        val view = LayoutInflater.from(context).inflate(R.layout.specie_item, parent, false)
        view.specie_text.text = specie.name

        return view
    }

    override fun getCount() = species.size

    override fun getItem(position: Int) = species[position]

    override fun getFilter() = filter

    private var filter: Filter = object : Filter() {

        override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
            val results = FilterResults()

            val query = if (constraint != null && constraint.isNotEmpty()) autocomplete(constraint.toString())
            else arrayListOf()

            results.values = query
            results.count = query.size

            return results
        }

        private fun autocomplete(input: String): ArrayList<Specie> {
            val results = arrayListOf<Specie>()

            for (specie in species) {
                if (specie.name.toLowerCase().contains(input.toLowerCase())) results.add(specie)
            }

            return results
         }

         override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults) {
            if (results.count > 0) notifyDataSetChanged()
            else notifyDataSetInvalidated()
         }

         override fun convertResultToString(result: Any) = (result as Specie).name
     }
}

The purpose of this adapter is to show some suggestions when we type something in a AutoCompleteTextview. To do this, I have a filter that search for species names according to the user input. The problem is that this filter is not working as I expected:

enter image description here Until here it's fine. It his showing all species names that start with agro

enter image description here But after typing something more, it is not filtering anymore. It should show species names that start with agrostis cas, but it is still showing the Agrostis azorica.

Is my filter bad? I have tried some other ways to filter, but I got exactly the same result.

Upvotes: 2

Views: 2145

Answers (1)

Xavier Falempin
Xavier Falempin

Reputation: 1206

What you are missing currently is your adapter does not take into account anything about your filter, you set it to depend on the full species list with your get count, getItemPosition.

Also you should take care of updating your text when views are recycled by your adapter and not set the value only when views are created.

Something like that should be better :

class SearchAdapter(private val activity: Activity, private var species: ArrayList<Specie>) : ArrayAdapter<Specie>(activity, R.layout.specie_item, species) {

    var filtered = ArrayList<Specie>()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        return convertView ?: createView(position, parent)
    }

    private fun createView(position: Int, parent: ViewGroup?): View {
        val view = LayoutInflater.from(context).inflate(R.layout.specie_item, parent, false)
        view?.name?.text = filtered[position].name
        return view
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View {
        convertView ?: LayoutInflater.from(context).inflate(R.layout.specie_item, parent, false)
        convertView?.name?.text = filtered[position].name
        return super.getDropDownView(position, convertView, parent)
    }

    override fun getCount() = filtered.size

    override fun getItem(position: Int) = filtered[position]

    override fun getFilter() = filter

    private var filter: Filter = object : Filter() {

        override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
            val results = FilterResults()

            val query = if (constraint != null && constraint.isNotEmpty()) autocomplete(constraint.toString())
            else arrayListOf()

            results.values = query
            results.count = query.size

            return results
        }

        private fun autocomplete(input: String): ArrayList<Specie> {
            val results = arrayListOf<Specie>()

            for (specie in species) {
                if (specie.name.toLowerCase().contains(input.toLowerCase())) results.add(specie)
            }

            return results
        }

        override fun publishResults(constraint: CharSequence?, results: Filter.FilterResults) {
            filtered = results.values as ArrayList<Specie>
            notifyDataSetInvalidated()
        }

        override fun convertResultToString(result: Any) = (result as Specie).name
    }
}

Upvotes: 2

Related Questions