Why filtering property not work for search bar in android?

I have a simple android project, there is a recycler view in fragment for user list, and I want to use search bar for those users, that is why I am using filtering for search the user in list view, everything is work but when I search user name, nothing change, and filtering property not work for users name. Any idea will be appreciated.

GameAdapter:

class GameAdapter(
    private val userId: String,
      var chatList: ArrayList<Game>

): RecyclerView.Adapter<GameAdapter.GamesHolder>(), Filterable {

    private var clickListener: ClickListener? = null
        var filteredUserDataList: MutableList<Game>

    inner class GamesHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
        val textGameTitle: TextView = view.findViewById(R.id.textUsername)
     
    }
   override fun onBindViewHolder(holder: GamesHolder, position: 
   Int) {
    val game = filteredUserDataList[position]
  holder.textGameTitle.text = 
 game.getGameName(userId).toString()
    }

  override fun getItemCount(): Int {
    return filteredUserDataList.size
}
override fun getFilter(): Filter {
    return filter
}

private var filter = object : Filter() {

    override fun performFiltering(keyword: CharSequence): FilterResults {
        val filtereddata = java.util.ArrayList<Game>()
        if (keyword.toString().isEmpty()) filtereddata.addAll(gameList) else {
            for (obj in gameList) {
                if (obj.getGameName(userId).toLowerCase()
                        .contains(keyword.toString().toLowerCase())
                ) filtereddata.add(obj)
            }
        }
        val results = FilterResults()
        results.values = filtereddata
        return results
    }

  
    override fun publishResults(constraint: CharSequence, results: FilterResults) {
        gameList.clear()
        gameList.addAll((results.values as ArrayList<Game>))
        notifyDataSetChanged()
    }

    init {
        filteredUserDataList = gameList
    }
}

fun getItem(index: Int): Game {
    return filteredUserDataList[index]
} 

GameFragment:

class GameFragment : Fragment() {

    lateinit var recyclerViewGame: RecyclerView

    var searchView: EditText? = null
    var search: CharSequence = ""

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val view = inflater.inflate(R.layout.fragment_games, container, false)

        recyclerViewGame = view.findViewById(R.id.game_list)

        configureRecycler(gameList)

        return view

    }

    private fun configureRecycler(games: ArrayList<Game>) {
        val userId = userPresenter.getGame().id
        val gamesAdapter = context?.let {
            GameAdapter(userId, it, games)
        }

        searchView = view?.findViewById(R.id.search_bar)

        searchView?.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
            override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
                gamesAdapter.filter.filter(charSequence)
                search = charSequence
            }
            override fun afterTextChanged(editable: Editable) {}
        })
     } }

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

I can see these things

  1. filteredUserDataList should be initialized with gameList

  2. change

    override fun getItemCount() = gameList.size

    to

    override fun getItemCount() = filteredUserDataList.size

  3. and remember that filteredUserDataList is the main list and the actual
    gameList is just to maintain the original data if the user clears or change the search query

Solution 2

You have check for userName not the userId. This is bit confusing for me because you are using userId inside predicate and userId is a global field in your case . I do not understand the role of userId here .

Anyway i am adding a solution below based on what i understood. give it a try

override fun performFiltering(charSequence: CharSequence): FilterResults {
            val searchTerm = charSequence.toString().lowercase(Locale.getDefault())
            filteredUserDataList = if(searchTerm.isEmpty()){
                gameList
            }else{
                val lstFiltered: ArrayList<Game> = ArrayList()
                for (row in gameList) {
                    val user = row.participants.find {
                        it.username.lowercase(Locale.getDefault()).contains(searchTerm)
                    }
                    if (user!=null) {
                        lstFiltered.add(row)
                    }
                }
                lstFiltered
            }
            val filterResults = FilterResults()
            filterResults.values = filteredUserDataList
            return filterResults
        }

 override fun publishResults(charSequence: CharSequence, filterResults: FilterResults) {
            filteredUserDataList = filterResults.values as ArrayList<Game>
            notifyDataSetChanged()
        }

In case if you are trying to search by two fields username and userId so you have to consider both when applying filter .

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply