I'm trying to show a Snackbar with a message whenever the dataset of a filtered RecyclerView contains 0 items but for some reason, the Snackbar doesn't appear at all. Does the relevant code need to go in the Fragment or Adapter? Can this be donw without libraries?
class MyFragment : androidx.fragment.app.Fragment() {
private lateinit var mRecyclerView: RecyclerView
private var mAdapter: MyAdapter? = null
private lateinit var mSearchView: SearchView
private lateinit var mSearchMenuItem: MenuItem
private val myList = ArrayList<Item>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.recycler_view, container, false)
mRecyclerView = view.findViewById(R.id.myRecyclerView)
mRecyclerView.setHasFixedSize(true)
mRecyclerView.layoutManager =
androidx.recyclerview.widget.LinearLayoutManager(this.activity)
mRecyclerView.addItemDecoration(
androidx.recyclerview.widget.DividerItemDecoration(
context, LinearLayout.VERTICAL
)
)
myList.add(
Item(
getString(R.string.item_a)
)
)
mAdapter = MyAdapter(requireActivity(), myList)
mRecyclerView.adapter = mAdapter
return view
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val mSnackbar = Snackbar.make(
requireView(),
getString(R.string.no_items),
Snackbar.LENGTH_LONG
)
mSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
mAdapter!!.filter.filter(newText)
mAdapter!!.notifyDataSetChanged()
if (mAdapter!!.itemCount <1) {
mSnackbar.show()
} else {
mSnackbar.dismiss()
}
return false
}
})
super.onCreateOptionsMenu(menu, inflater)
}
}
Adapter code
class MyAdapter(
private val mCtx: Context,
var myList: MutableList<Item>,
) : androidx.recyclerview.widget.RecyclerView.Adapter<MyAdapter.MyViewHolder>(), Filterable {
private val myListFull = myList.toMutableList()
val mSnackbar = Snackbar.make(
requireView(),
getString(R.string.my_message),
Snackbar.LENGTH_INDEFINITE
)
private val companyFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = ArrayList<Item>()
if (constraint == null || constraint.isEmpty()) {
filteredList.addAll(myListFull)
} else {
val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in myListFull) {
if (item.Name.toLowerCase().contains(filterPattern)
) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
myList.clear()
myList.addAll(results!!.values as List<Item>)
notifyDataSetChanged()
if (filteredList.isEmpty()) {
mSnackbar.show()
} else {
mSnackbar.dismiss()
}
}
}
private fun String.matchesIgnoreCase(otherString: String): Boolean {
return this.toLowerCase().contains(otherString.trim().toLowerCase())
}
class MyViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
.ViewHolder(itemView) {
var tvTitle: TextView = itemView.findViewById(R.id.title)
var tvSubtitle: TextView = itemView.findViewById(R.id.subtitle)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(mCtx)
val v = inflater.inflate(R.layout.list_item, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return myList.size
}
override fun getFilter(): Filter {
return companyFilter
}
}
I'm guessing it's this:
Filtering operations performed by calling
filter(java.lang.CharSequence)
orfilter(java.lang.CharSequence, android.widget.Filter.FilterListener)
are performed asynchronously. When these methods are called, a filtering request is posted in a request queue and processed later.
So you're checking myList
(through getItemCount
) immediately after calling filter
, and the data hasn't updated yet because the filter / publish stuff hasn't finished running. So your snackbar gets dismissed
The publishResults
method runs on the UI thread, so maybe display the snackbar there, if you need to?