how implement searchview with recyclerview correctly? - android

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

Related

How to use checkbox with select all in Recyclerview android

In my application I want use checkbox in recyclerview.
I want when users click on item check/unchecked checkbox and when click on Select All or clear, checked/unchecked all of items.
I write below codes, when click on select all checked all of checkboxes but after click on one of checkbox not unchecked!
I should click twice on checkbox after unchecked!
My UI is :
My Adapter codes :
class DataListAdapter #Inject constructor() : RecyclerView.Adapter<DataListAdapter.ViewHolder>() {
private lateinit var binding: ItemWithCheckboxBinding
private lateinit var context: Context
private var moviesList = emptyList<String>()
private var isSelectedAll = false
private var checkBoxState = SparseBooleanArray()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
binding = ItemWithCheckboxBinding.inflate(LayoutInflater.from(parent.context), parent, false)
context = parent.context
return ViewHolder()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(moviesList[position])
holder.checkBox.isChecked = checkBoxState.get(position, false)
var state: String
if (!isSelectedAll) {
holder.checkBox.isChecked = false
state = LIST_STATE_REMOVE
onItemClickListener?.let { it(moviesList[position], state) }
} else {
holder.checkBox.isChecked = true
state = LIST_STATE_ADD
onItemClickListener?.let { it(moviesList[position], state) }
}
}
override fun getItemCount() = moviesList.size
inner class ViewHolder : RecyclerView.ViewHolder(binding.root) {
val checkBox = binding.itemCheck
#SuppressLint("SetTextI18n")
fun bind(item: String) {
binding.apply {
//Views
itemTitle.text = item
//Click
var state: String
binding.root.setOnClickListener {
if (!checkBoxState.get(adapterPosition, false)) {
checkBox.isChecked = true
checkBoxState.put(adapterPosition, true)
state = LIST_STATE_ADD
} else {
checkBox.isChecked = false
checkBoxState.put(adapterPosition, false)
state = LIST_STATE_REMOVE
}
onItemClickListener?.let { it(item, state) }
}
}
}
}
#SuppressLint("NotifyDataSetChanged")
fun selectAll() {
isSelectedAll = true
notifyDataSetChanged()
}
#SuppressLint("NotifyDataSetChanged")
fun unSelectAll() {
isSelectedAll = false
notifyDataSetChanged()
}
private var onItemClickListener: ((String, String) -> Unit)? = null
fun setOnItemClickListener(listener: (String, String) -> Unit) {
onItemClickListener = listener
}
fun setData(data: List<String>) {
val moviesDiffUtil = NotesDiffUtils(moviesList, data)
val diffUtils = DiffUtil.calculateDiff(moviesDiffUtil)
moviesList = data
diffUtils.dispatchUpdatesTo(this)
}
class NotesDiffUtils(private val oldItem: List<String>, private val newItem: List<String>) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldItem.size
}
override fun getNewListSize(): Int {
return newItem.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItem[oldItemPosition] === newItem[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItem[oldItemPosition] === newItem[newItemPosition]
}
}
}
For control select all and clear checkboxes I write codes in Fragment:
selectAllTxt.setOnClickListener { dataListAdapter.selectAll() }
clearTxt.setOnClickListener { dataListAdapter.unSelectAll() }
How can I fix it?
Try to use the !checkBox.isChecked instead of !checkBoxState.get(adapterPosition, false) when clicking:
binding.root.setOnClickListener {
if (!checkBox.isChecked) {
checkBox.isChecked = true
checkBoxState.put(adapterPosition, true)
state = LIST_STATE_ADD
} else {
checkBox.isChecked = false
checkBoxState.put(adapterPosition, false)
state = LIST_STATE_REMOVE
}
onItemClickListener?.let { it(item, state) }
}

searchview using retrofit android

I'm referring to this tutorial.
Everything runs fine without error but its not filtering
when I type anything. It is not filtering the list.
I don't know what I'm doing wrong.
The following is my adapter and activity code.
Adapter code
class Table_Adapter(val context: Context) : RecyclerView.Adapter<Table_Adapter.MyViewHolder>(),Filterable {
var mFilteredList: MutableList<Tabledata> = mutableListOf()
var Tablelist: MutableList<Tabledata> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.table_recycle_item,
parent,
false
)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return Tablelist.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.productname.text = Tablelist.get(position).name
holder.producername.text = Tablelist.get(position).producer
holder.productprice.text = Tablelist.get(position).cost.toString()
Glide.with(context).load(Tablelist.get(position).product_images)
.into(holder.image)
holder.rate.setRating(Tablelist.get(position).rating.toFloat());
holder.itemView!!.setOnClickListener {
val context:Context=holder.itemView.context
val i=Intent(
context,
Product_details::class.java
)
i.putExtra("id", Tablelist.get(position).id.toString())
i.putExtra("image", Tablelist.get(position).product_images)
context.startActivity(i)
}
}
fun setMovieListItems(movieList: MutableList<Tabledata>){
this.Tablelist = movieList;
notifyDataSetChanged()
}
class MyViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
val productname: TextView = itemView!!.findViewById(R.id.title)
val producername: TextView = itemView!!.findViewById(R.id.title1)
val productprice: TextView = itemView!!.findViewById(R.id.title2)
val rate: RatingBar=itemView!!.findViewById(R.id.ratingbar)
val image: ImageView = itemView!!.findViewById(R.id.image)
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults? {
val charString = charSequence.toString()
if (charString.isEmpty()) {
mFilteredList = Tablelist
} else {
val filteredList: MutableList<Tabledata> = ArrayList()
for (androidVersion in Tablelist) {
/* if (androidVersion.name.toLowerCase()
.contains(charString)
) {
filteredList.add(androidVersion)
}*/
if (androidVersion.name.toLowerCase()
.contains(charString.toLowerCase()) || androidVersion.name
.contains(charSequence)
) {
filteredList.add(androidVersion)
}
}
mFilteredList = filteredList
}
val filterResults = FilterResults()
filterResults.values = mFilteredList
return filterResults
}
override fun publishResults(charSequence: CharSequence?, filterResults: FilterResults) {
mFilteredList = filterResults.values as MutableList<Tabledata>
notifyDataSetChanged()
}
}
}
}
Activity:
class Tables : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
lateinit var recyclerAdapter: Table_Adapter
var Tablelist : MutableList<Tabledata> = mutableListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.table_activity)
var mActionBarToolbar = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbartable);
setSupportActionBar(mActionBarToolbar);
if (getSupportActionBar() != null){
getSupportActionBar()?.setDisplayHomeAsUpEnabled(true);
getSupportActionBar()?.setDisplayShowHomeEnabled(true);
getSupportActionBar()?.setHomeAsUpIndicator(R.drawable.ic_keyboard_arrow_left_black_24dp);
getSupportActionBar()?.setTitle((Html.fromHtml("<font color=\"#FFFFFF\">" + getString(R.string.Tables) + "</font>")));
}
recyclerView = findViewById(R.id.recyleview)
recyclerAdapter = Table_Adapter(this)
recyleview.layoutManager = LinearLayoutManager(this)
recyclerView.addItemDecoration(
DividerItemDecoration(
recyclerView.context,
DividerItemDecoration.VERTICAL
)
)
recyleview.adapter = recyclerAdapter
RetrofitClient.instancetable.getAllPhotos(product_category_id = "1", value = 1).enqueue(
object : Callback<Table_response> {
override fun onFailure(call: Call<Table_response>, t: Throwable) {
Toast.makeText(applicationContext, "falied", Toast.LENGTH_LONG).show()
}
override fun onResponse(
call: Call<Table_response>,
response: Response<Table_response>
) {
if (response?.body() != null) {
recyclerAdapter.setMovieListItems((response.body()?.data as MutableList<Tabledata>?)!!)
}
}
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
NavUtils.navigateUpFromSameTask(this)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
val search: MenuItem = menu.findItem(R.id.search)
val searchView: SearchView = MenuItemCompat.getActionView(search) as SearchView
search(searchView)
return true
}
override fun onBackPressed() {
super.onBackPressed()
}
private fun search(searchView: SearchView) {
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
recyclerAdapter.getFilter().filter(newText)
return true
}
})
}
}
Thanks in advance.
Search view queries do not result in a filtered list because you are not using the filtered list.
onBindViewHolder and getItemCount uses only Tablelist variable. mFilteredList only holds filtered results - not uses them.
Store original list as you do it currently, but replace the use of Tablelist with mFilteredList in onBindViewHolder and getItemCount. Also, a minor update in setMovieListItems is required. mFilteredList is also declared as unmutable. performFiltering also was improved a little bit and is not shorter. Everything is presented below:
class Table_Adapter() : RecyclerView.Adapter<Table_Adapter.MyViewHolder>(), Filterable {
var mFilteredList: List<Tabledata> = listOf()
var tableList: MutableList<Tabledata> = mutableListOf()
...
override fun getItemCount(): Int {
return mFilteredList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val tableItem = mFilteredList.get(position)
holder.productname.text = tableItem.name
holder.producername.text = tableItem.producer
holder.productprice.text = tableItem.cost.toString()
Glide.with(holder.itemView!!.context).load(tableItem.product_images)
.into(holder.image)
holder.rate.setRating(tableItem.rating.toFloat());
holder.itemView!!.setOnClickListener {
val context: Context = holder.itemView.context
val i = Intent(
context,
Product_details::class.java
)
i.putExtra("id", tableItem.id.toString())
i.putExtra("image", tableItem.product_images)
context.startActivity(i)
}
}
fun setMovieListItems(movieList: MutableList<Tabledata>) {
tableList = movieList.toMutableList() // makes a copy
mFilteredList = movieList
notifyDataSetChanged()
}
...
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults? {
val queryString = charSequence.toString()
val filterResults = FilterResults()
filterResults.values =
if (queryString.isEmpty()) {
tableList
} else {
tableList.filter {
it.name.contains(queryString, ignoreCase = true) || it.name.contains(charSequence)
}
}
return filterResults
}
override fun publishResults(charSequence: CharSequence?, filterResults: FilterResults) {
mFilteredList = filterResults.values
notifyDataSetChanged()
}
}
}
}

Onclick in recyclerView wrong positions

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)
}

Issues implementing search on recycleview kotlin

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?

MVVM Recyclerview Livedata Room SearchView

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)
}

Categories

Resources