I'm trying to implement search option in recyclerview.
What I have implemented so far is:
created a search bar as below:
I've applied onCreateOptions as per below in MainActivity:
class RecyclerListActivity: AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: PostListViewModel
private var errorSnackbar: Snackbar? = null
private var searchView: SearchView? = null
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.postList.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false)
viewModel = ViewModelProviders.of(this).get(PostListViewModel::class.java)
viewModel.errorMessage.observe(this, Observer {
errorMessage -> if(errorMessage != null) showError(errorMessage) else hideError()
})
binding.viewModel = viewModel
}
private fun showError(#StringRes errorMessage:Int){
errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
errorSnackbar?.show()
}
private fun hideError(){
errorSnackbar?.dismiss()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
searchView = menu.findItem(R.id.action_search).actionView as SearchView
searchView!!.maxWidth = Int.MAX_VALUE
searchView!!.imeOptions = EditorInfo.IME_ACTION_DONE
// listening to search query text change
searchView!!.setOnQueryTextListener(object :
SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(query: String): Boolean {
return false
}
})
return true
}
}
My Adapter class as below:
class PostListAdapter: RecyclerView.Adapter<PostListAdapter.ViewHolder>(), Filterable {
private lateinit var postList:List<Data>
private lateinit var postListFull:List<Data>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ListItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.list_item, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(postList[position])
}
override fun getItemCount(): Int {
return if(::postList.isInitialized) postList.size else 0
}
fun updatePostList(postList:List<Data>){
this.postList = postList
notifyDataSetChanged()
}
class ViewHolder(private val binding: ListItemBinding): RecyclerView.ViewHolder(binding.root){
private val viewModel = MoviesViewModel()
fun bind(post:Data){
viewModel.bind(post)
binding.viewModel = viewModel
}
init {
binding.root.setOnClickListener {
//Toast.makeText(binding.root.context, binding.postTitle.text, Toast.LENGTH_SHORT).show()
val intent = Intent(binding.root.context, DetailsActivity::class.java)
//intent.putExtra(REPO_NAME, binding.postTitle.text)
binding.root.context.startActivity(intent)
}
}
}
override fun getFilter(): Filter? {
return searchFilter
}
private val searchFilter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: MutableList<Data> = ArrayList()
if (constraint == null || constraint.isEmpty()) {
filteredList.addAll(postListFull)
} else {
val filterPattern =
constraint.toString().toLowerCase().trim()
for (item in postListFull) {
if (item.title.toLowerCase().contains(filterPattern) || item.genre.toLowerCase().contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(
constraint: CharSequence?,
results: FilterResults
) {
//postList.clear()
//postList.addAll(results.values as List<*>)
notifyDataSetChanged()
}
}
I have two issues here to resolve it. first one is: getting unresolved reference in these lines (Adapter class). How could I resolve it?
postList.clear()
postList.addAll(results.values as List<*>)
second one is: how to apply the filter results in adapter as I've used dagger & databinding? I've used following tutorial to create recyclerview: https://proandroiddev.com/mvvm-with-kotlin-android-architecture-components-dagger-2-retrofit-and-rxandroid-1a4ebb38c699
private lateinit var postListFull: ArrayList<Data>= ArrayList()
in the publishResults() method, to store the result in list :
postListFull= results!!.values as ArrayList<Data>
notifyDataSetChanged()
for your second issue, where do you want to apply the filter results and why?
Related
I am trying to filter my recyclerview using filterable, it works fine on static data, but on dynamic data from firestore
First, I got the data from Firestore in arrayList
Then, I filter those arrayList to new arrayList
But when any change happended in Firestore, the original arrayList will be updated and my recyclerview will display this data instead the data I currently filltered on searchview
What I wanted to do is my recyclerview to aslway display filtered data when I type any word on searchview whether they are any updated data or not
added screenshot
here is my adapter
class SearchAdapter() : RecyclerView.Adapter<SearchAdapter.ListViewHolder>(), Filterable {
private var listSearch = ArrayList<Dosen>()
private var listSearchFull = ArrayList<Dosen>()
fun setData(list: ArrayList<Dosen>){
this.listSearch = list
listSearchFull = ArrayList(listSearch)
}
inner class ListViewHolder(itemView: UserListBinding) : RecyclerView.ViewHolder(itemView.root) {
private val binding = itemView
fun bind(dosen: Dosen) {
with(binding){
val db = Firebase.firestore
val collection = db.collection("alat")
.whereEqualTo("id", dosen.alat_id)
collection.get()
.addOnSuccessListener { document ->
try {
val location = document.toObjects(Alat::class.java)[0].lokasi
tvLocation.text = location
} catch (e: Exception){
Log.d("rv", "system error $e")
}
}
.addOnFailureListener { exception ->
Log.d("rv", "get failed with ", exception)
}
tvUsername.text = dosen.nama
val simpleDateFormat = SimpleDateFormat("EEEE, dd LLLL yyyy")
val date = simpleDateFormat.format(dosen.datetime!!.toDate())
Log.d("rvTime", date)
tvDate.text = date
val simpleTimeFormat = SimpleDateFormat("KK:mm:ss aaa")
val time = simpleTimeFormat.format(dosen.datetime.toDate())
Log.d("rvTime", time)
tvTime.text = time
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
return ListViewHolder(UserListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
holder.bind(listSearch[position])
}
override fun getItemCount(): Int = listSearch.size
override fun getFilter(): Filter {
return object : Filter(){
override fun performFiltering(query: CharSequence?): FilterResults {
val filteredList: ArrayList<Dosen> = ArrayList()
if (query == null || query.length == 0){
filteredList.addAll(listSearchFull)
} else{
val searchQuery = query.toString().toLowerCase().trim()
for (item in listSearchFull) {
if (item.nama!!.lowercase(Locale.ROOT).contains(searchQuery)){
filteredList.add(item)
}
}
}
val filterResults = FilterResults()
filterResults.values = filteredList
return filterResults
}
override fun publishResults(query: CharSequence?, filteredResult: FilterResults?) {
//error
listSearch.clear()
listSearch.addAll(filteredResult!!.values as ArrayList<Dosen>)
notifyDataSetChanged()
}
}
}
}
and here is my fragment
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
private val binding get() = _binding!!
private lateinit var searchAdapter: SearchAdapter
private lateinit var searchArrayList : ArrayList<Dosen>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentSearchBinding.inflate(inflater, container, false)
val root: View = binding.root
searchArrayList = arrayListOf()
#Suppress("DEPRECATION")
setHasOptionsMenu(true)
eventChangeListener()
return root
}
private fun eventChangeListener() {
val db = Firebase.firestore
db.collection("presensi")
.orderBy("datetime", Query.Direction.DESCENDING)
.addSnapshotListener(object : EventListener<QuerySnapshot>{
override fun onEvent(value: QuerySnapshot?, error: FirebaseFirestoreException?) {
searchArrayList.clear()
for (document in value!!){
searchArrayList.add(document.toObject(Dosen::class.java))
searchAdapter.setData(searchArrayList)
Log.d("arraySearch", document.toObject<Dosen>().toString())
Log.d("arraySearchList", searchArrayList.toString())
}
searchAdapter.notifyDataSetChanged()
}
})
searchAdapter = SearchAdapter()
with(binding){
tvNoData.visibility = View.GONE
rvSearch.layoutManager = LinearLayoutManager(activity)
rvSearch.setHasFixedSize(true)
rvSearch.adapter = searchAdapter
}
}
/// prob need fix
#Deprecated("Deprecated in Java")
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
#Suppress("DEPRECATION")
super.onCreateOptionsMenu(menu, inflater)
menu.clear()
inflater.inflate(R.menu.main_menu, menu)
val searchView = context?.let { SearchView(it) }
menu.findItem(R.id.menu_search).apply {
setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW or MenuItem.SHOW_AS_ACTION_IF_ROOM)
actionView = searchView
}
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
searchAdapter.filter.filter(newText)
return false
}
})
}
override fun onStart() {
super.onStart()
Log.d("firebaseFirestoreListener", "onStart")
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
Log.d("firebaseFirestoreListener", "onDestroyView")
}
}
Is it actually possible to do what I want?
or should I use 3rd party library like algolia, but it's not free.
I hope you guys understand my question
First of all, I am Spanish so my english is not very good.
I have a list of items on a Recyclerview, and I also have a SearchView to filter those items.
Every item has a favourite button, so when you click, the item adds to favorite table.
The problem is that, when I filter something and I start clicking those buttons, odd things happens: some items dissapear from the filtered list. It doesn't happen always, only sometimes. How can I fix this?
My class:
class CoasterFragment : Fragment() {
private val myAdapter by lazy { CoasterRecyclerViewAdapter(CoasterListenerImpl(requireContext(), viewModel),requireContext()) }
private lateinit var searchView: SearchView
private var _binding: FragmentCoasterBinding? = null
private val binding get() = _binding!!
private val viewModel: CoastersViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentCoasterBinding.inflate(inflater, container, false)
val root: View = binding.root
val recyclerView = binding.recyclerCoaster
recyclerView.adapter = myAdapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
viewModel.coasters().observe(viewLifecycleOwner){myAdapter.setData(it)}
searchView = binding.search
searchView.clearFocus()
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
if(query != null){
searchDatabase(query)
searchView.clearFocus()
}
return true
}
override fun onQueryTextChange(query: String?): Boolean {
if(query != null){
searchDatabase(query)
}
return true
}
})
return root
}
fun searchDatabase(query: String) {
val searchQuery = "%$query%"
viewModel.searchDatabase(searchQuery).observe(viewLifecycleOwner) { myAdapter.setData(it)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
My adapter:
class CoasterRecyclerViewAdapter( val listener: CoasterListener,
val context: Context ) : RecyclerView.Adapter<CoasterRecyclerViewAdapter.ViewHolder>(){
private var coasterList = emptyList<CoasterFavorito>()
class ViewHolder private constructor(val binding: CoasterItemBinding, private val listener: CoasterListener,
private val context: Context): RecyclerView.ViewHolder(binding.root){
companion object{
fun crearViewHolder(parent: ViewGroup, listener: CoasterListener, context: Context):ViewHolder{
val layoutInflater = LayoutInflater.from(parent.context)
val binding = CoasterItemBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding, listener, context )
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder.crearViewHolder(parent, listener, context)
override fun onBindViewHolder(holder: ViewHolder, position: Int){
holder.binding.nombre.text = coasterList[position].coaster.nombre
holder.binding.parque.text = coasterList[position].coaster.parque
holder.binding.ciudad.text = coasterList[position].coaster.ciudad
holder.binding.provincia.text = coasterList[position].coaster.provincia
holder.binding.comunidad.text = coasterList[position].coaster.comunidadAutonoma
Glide
.with(context)
.load(coasterList[position].coaster.imagen)
.centerCrop()
.into(holder.binding.imagen)
holder.binding.check.isChecked = coasterList[position].favorito
holder.binding.check.setOnClickListener{
if (coasterList[position].favorito) {
listener.delFavorito(coasterList[position].coaster.id)
holder.binding.check.isChecked = false
} else {
listener.addFavorito(coasterList[position].coaster.id)
holder.binding.check.isChecked = true
}
}
}
override fun getItemCount(): Int{
return coasterList.size
}
fun setData(coaster: List<CoasterFavorito>){
coasterList = coaster
notifyDataSetChanged()
}
}
interface CoasterListener {
fun addFavorito(id: Long)
fun delFavorito(id: Long)
}
I tried changing the focus, changing the notifydatasetchanged with notifyitemchanged, and nothing happens...
I have implemented searchView with recyclerView it is not working and giving errors I did not understand where I am making mistakes. below my MainActivity where I have implemented searchview logic
I have implemented searchView with recyclerView it is not working and giving errors I did not understand where I am making mistakes. below my MainActivity where I have implemented searchview logic
class MemberActivity : AppCompatActivity() {
private var memberAdapter: MemberAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_member)
val compositeDisposable = CompositeDisposable()
compositeDisposable.add(
ServiceBuilder.buildService(SpectrumInterface::class.java)
.getMembers()
.toObservable()
.flatMap { Observable.fromIterable(it) }
.flatMap { Observable.fromIterable(it.members) }
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ members -> onResponse(members) },
{ t -> onFailure(t) })
)
memberAdapter = MemberAdapter()
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayout.VERTICAL))
private fun onFailure(t: Throwable) {
Toast.makeText(this, t.message, Toast.LENGTH_SHORT).show()
}
private fun onResponse(members: List<Member>) {
progressBar.visibility = View.GONE
(recyclerView.adapter as MemberAdapter).setMembers(members)
}
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
memberAdapter?.getFilter()?.filter(newText)
return true
}
ascendingButton.setOnClickListener
{
memberAdapter?.sortAscending()
}
descendingButton.setOnClickListener {
memberAdapter?.sortDescending()
}
}
}
below my Adapter where I have implemented filter logic
class MemberAdapter : RecyclerView.Adapter<MemberAdapter.MemberViewHolder>() {
private val members = mutableListOf<Member>()
fun setMembers(data: List<Member>) {
members.clear()
members.addAll(data)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemberViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.member_list, parent, false)
return MemberViewHolder(view)
}
override fun getItemCount() = members.size
fun sortAscending() {
members.sortBy { it.age }
notifyDataSetChanged()
}
fun sortDescending() {
members.sortByDescending { it.age }
notifyDataSetChanged()
}
fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults {
val query = charSequence.toString()
val filtered: MutableList<Member> = ArrayList()
if (query.isEmpty()) {
filtered.clear()
filtered.addAll(members)
} else {
filtered.addAll(members.filter {
it.id.toLowerCase(Locale.ROOT)
.contains(query.toLowerCase(Locale.ROOT)) || it.age.toString()
.contains(query)
})
}
val results = FilterResults()
results.count = filtered.size
results.values = filtered
return results
}
override fun publishResults(charSequence: CharSequence, results: FilterResults) {
members.clear()
members.addAll(results.values as Collection<Member>)
notifyDataSetChanged()
}
}
}
override fun onBindViewHolder(holder: MemberViewHolder, position: Int) {
return holder.bind(members[position])
}
class MemberViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val memberAge: TextView = itemView.findViewById(R.id.memberAge)
// private val memberName: TextView = itemView.findViewById(R.id.memberName)
private val lastName: TextView = itemView.findViewById(R.id.lastName)
private val firstName: TextView = itemView.findViewById(R.id.firstName)
private val emailAddress: TextView = itemView.findViewById(R.id.emailAddress)
private val phone: TextView = itemView.findViewById(R.id.phone)
fun bind(member: Member) {
memberAge.text = member.age.toString()
lastName.text = member.name?.last
firstName.text = member.name?.first
emailAddress.text = member.email
phone.text = member.phone
}
}
}
Your mistake is updating members and then using it again as your base for future filtering.
Instead:
Create another list filteredMembers in MembersAdapter, initialize it to be equal to members in setMembers()
Make MembersAdapter reflect the items in filteredMembers
override fun getItemCount() = filteredMembers.size
override fun onBindViewHolder(holder: MemberViewHolder, position: Int) {
return holder.bind(filteredMembers[position])
}
Make your filter update filteredMembers and not members
I have a recyclerView with 10 names and in this activity is possible search for name. With a click in a name the activity moves to another(with help Intents to move for DetailsActivity). It possible make this because in adapter, i have a function that give the position that the user have clicked. But when put a word in menu search this functional gives the position 1 (the real position of name, in exactally moment).
I can send in function in adpter for example, the id of name? What is the correct way to make this?
MainActivity.kt
class MainActivity : AppCompatActivity(), PeopleInStarWarsAdapter.OnNoteListener {
private lateinit var mainViewModel: MainViewModel
private var listDataPeople = ArrayList<DataPeople>()
private lateinit var dataPeopleAdapter: PeopleInStarWarsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainViewModel = ViewModelProvider
.AndroidViewModelFactory
.getInstance(application)
.create(MainViewModel::class.java)
//iniciar ViewModel
mainViewModel.init(this)
mainViewModel.getList().observe(this, Observer{ it ->
if(it != null){
listDataPeople = it
dataPeopleAdapter.submitList(listDataPeople)
}
else{
Toast.makeText(this, "Something is wrong!", Toast.LENGTH_SHORT).show()
}
})
initRecyclerView(listDataPeople)
}
private fun initRecyclerView(listDataPeople : ArrayList<DataPeople>) {
recycler_view.layoutManager = LinearLayoutManager(this)
dataPeopleAdapter = PeopleInStarWarsAdapter(listDataPeople, this)
recycler_view.adapter = dataPeopleAdapter
}
override fun onNoteClick(position: Int){
val intent = Intent(this, DetailsActivity::class.java)
intent.putExtra(BuildConfig.POSITION, position)
startActivity(intent)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
val manager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
val searchItem = menu?.findItem(R.id.search)
val searchView = searchItem?.actionView as SearchView
searchView.setSearchableInfo(manager.getSearchableInfo(componentName))
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String): Boolean {
searchView.clearFocus()
searchView.setQuery("", false)
searchItem.collapseActionView()
Log.v("Information", "Looking for $query")
mainViewModel.checkMatch(query, this#MainActivity)
return true
}
override fun onQueryTextChange(newText: String): Boolean {
Log.v("Information", "$newText")
return false
}
})
return true
}
}
PeopleInStarWarsAdapter.kt
class PeopleInStarWarsAdapter(listDataPeople: ArrayList<DataPeople>, onNoteListener: OnNoteListener) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
var listDataPeople = ArrayList<DataPeople>()
private var mOnNoteListener : OnNoteListener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.layout_list_item, parent,
false)
)
}
override fun getItemCount(): Int {
return listDataPeople.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolder -> {
holder.ViewHolder(listDataPeople[position], mOnNoteListener)
}
}
}
fun submitList(dataPeopleList: ArrayList<DataPeople>) {
this.listDataPeople = dataPeopleList
notifyDataSetChanged()
}
class ViewHolder constructor(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener {
private val textViewName : TextView = itemView.textView_name
var onNoteListener: OnNoteListener? = null
fun ViewHolder(dataPeople: DataPeople, onNoteListener: OnNoteListener) {
val name = dataPeople.name
this.textViewName.text = name
this.onNoteListener = onNoteListener
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
onNoteListener?.onNoteClick(adapterPosition)
}
}
interface OnNoteListener {
fun onNoteClick(position: Int)
}
init {
this.listDataPeople = listDataPeople
mOnNoteListener = onNoteListener
}
}
You should send position to ViewHolder .
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolder -> {
holder.ViewHolder(listDataPeople[position], mOnNoteListener,position)
}
}
}
Then pass this value into onNoteClick()
fun ViewHolder(dataPeople: DataPeople, onNoteListener: OnNoteListener,position: Int) {
val name = dataPeople.name
this.textViewName.text = name
this.onNoteListener = onNoteListener
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
onNoteListener?.onNoteClick(position)
}
I am making database app using room, mvvm, livedata . I have Prepopulated it with some data. Now i have two options either i am going to show that prepoulated data when app turns on via recyclerview or show it when i search it using SearchView inside recyclerView.
The Problem is when i search for particular item, itw shows that item when i complete my word and when i try to go back either my resView back empty or it always stays on that item. i wanna try sometime realtime like: when i enter only one alphabet it show all the suggestions belongs to alphabet, i only want to update with livedata
What i have try?
1-> I have already tried switchMap which worked with liveData but i have to refresh my activity in order to get back my list.
2-> I have tried resView filter which didn't worked because i am using livedata to update my UI and also give it regular try without livedata it still didn't work either.
3-> I Have tried regular editText just to try but i didn't find it useful as facing same problem, my main focus was on searchView
RecyclerView code with filterable or just ignore filterable part it wasn't good try at all
class MyAdapter(
private var context: Context,
private var dataList: List<SearchPojo>
) : RecyclerView.Adapter<MyAdapter.BaseViewHolder<*>>(), Filterable {
private var exampleListFull: List<SearchPojo>? = null
init {
exampleListFull = ArrayList(dataList)
}
companion object {
const val SEARCH_TYPE = 1
}
abstract class BaseViewHolder<T>(view: View) : RecyclerView.ViewHolder(view) {
abstract fun bind(t: T)
}
inner class SearchViewHolder(view: View) : BaseViewHolder<SearchPojo>(view) {
private val userID: TextView = view.findViewById(R.id.idSearch)
private val userName: TextView = view.findViewById(R.id.nameSearch)
private val userPos: TextView = view.findViewById(R.id.positionSearch)
override fun bind(t: SearchPojo) {
userID.text = t.id
userName.text = t.userName
userPos.text = t.position
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
return when (viewType) {
SEARCH_TYPE -> {
val view =
LayoutInflater.from(context).inflate(R.layout.layout_show_data, parent, false)
SearchViewHolder(view)
}
else -> {
throw IllegalAccessException("In valid View Type")
}
}
}
override fun getItemCount(): Int {
return dataList.size
}
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
val element = dataList[position]
when (holder) {
is SearchViewHolder -> {
holder.bind(element)
}
}
}
override fun getItemViewType(position: Int): Int {
return when (dataList[position]) {
is SearchPojo -> SEARCH_TYPE
else -> throw IllegalAccessException()
}
}
override fun getFilter(): Filter {
return exampleFilter
}
private var exampleFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = ArrayList<SearchPojo>()
if (constraint == null || constraint.isEmpty()) {
filteredList.addAll(exampleListFull as Iterable<SearchPojo>)
} else {
val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in exampleListFull!!) {
if (item.userName.toLowerCase().contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(constraint: CharSequence, results: FilterResults) {
dataList.clear()
dataList.addAll(results.values as List<SearchPojo>)
notifyDataSetChanged()
}
}
}
Main Activity
class MainActivity : AppCompatActivity() {
lateinit var searchViewModel: SearchViewModel
lateinit var myAdapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!::searchViewModel.isInitialized) {
searchViewModel = ViewModelProviders.of(this)[SearchViewModel::class.java]
searchViewModel.getAll().observe(this, Observer {
myAdapter(it)
})
}
searchItems.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
nameFromDb(s.toString())
}
})
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.search_item, menu)
val searchItem = menu!!.findItem(R.id.search_menu)
val searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
myAdapter.getFilter().filter(newText)
return true
}
})
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.refresh -> {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
return true
}
private fun nameFromDb(searchTxt: String) {
searchViewModel = ViewModelProviders.of(this)[SearchViewModel::class.java]
searchViewModel.items.observe(this, object : Observer<List<SearchPojo>> {
override fun onChanged(t: List<SearchPojo>?) {
if (t == null) {
return
}
myAdapter(t)
}
})
searchViewModel.searchIt(searchTxt)
}
private fun myAdapter(t: List<SearchPojo>) {
searchResultResView.apply {
layoutManager = LinearLayoutManager(context)
myAdapter = MyAdapter(this#MainActivity, t)
adapter = myAdapter
}
}
}
ViewModel
lass SearchViewModel(application: Application) :
AndroidViewModel(application) {
private val repo: SearchRepo = SearchRepo(application)
private val _searchItem : MutableLiveData<String> = MutableLiveData()
val items : LiveData<List<SearchPojo>> = Transformations.switchMap(_searchItem) { myItems ->
repo.searchItem(myItems)
}
init {
_searchItem.value = ""
}
fun searchIt(items: String) {
_searchItem.value = items
}
fun getAll() = repo.allSearch()
}
Repository
class SearchRepo(application: Application) {
private val searchDao: SearchDao
init {
val db = SearchDb.instance(application)
searchDao = db.searchDao()
}
fun searchItem(id: String): LiveData<List<SearchPojo>> {
return searchDao.searchViaID(id)
}
fun allSearch() : LiveData<List<SearchPojo>> {
return searchDao.allSearch()
}
}
Dao
#Dao
abstract class SearchDao : BaseDao<SearchPojo> {
#Query("Select * from SearchPojo")
abstract fun allSearch(): LiveData<List<SearchPojo>>
#Query("Select * from SearchPojo where userName Like :userNameSearch or LOWER(userName) like LOWER(:userNameSearch)")
abstract fun searchViaID(userNameSearch: String) : LiveData<List<SearchPojo>>
#Insert
abstract override fun insert(searchPojo: SearchPojo)
}
Please change your #Dao class like this
#Dao
abstract class SearchDao : BaseDao<SearchPojo> {
#Query("Select * from SearchPojo")
abstract fun allSearch(): LiveData<List<SearchPojo>>
#Query("Select * from SearchPojo where userName GLOB '*' || :userNameSearch|| '*'")
abstract fun searchViaID(userNameSearch: String) : LiveData<List<SearchPojo>>
#Insert
abstract override fun insert(searchPojo: SearchPojo)
}