MVVM Recyclerview Livedata Room SearchView - android

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

Related

highlight filter text in recyclerview using searchview

I have created recyclerview with searchview, but I want to highlight the text in recyclerview which I enter in the searchview. I have tried some mehtod in onviewholder but it didn't work. Is there any way to do it? I have share screenshot and code below, please help me to do it.
bookadapter.kt
class bookadapter( private var booklist:ArrayList<Booksmodel>, private val itemClickListener:
OnItemClicklistner):RecyclerView.Adapter<bookadapter.bookholder>() {
val searchText:String?=null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): bookholder {
val view=LayoutInflater.from(parent.context).inflate(R.layout.singlebook,parent,false)
return bookholder(view)
}
fun filterlist(filterlist:ArrayList<Booksmodel>){
booklist=filterlist
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: bookholder, position: Int) {
val view=booklist[position]
holder.bind(view,itemClickListener)
}
override fun getItemCount(): Int {
return booklist.size
}
inner class bookholder(view: View):RecyclerView.ViewHolder(view){
val bookname:TextView=view.findViewById(R.id.recbooknametxt)
val bookpublication=view.findViewById<TextView>(R.id.recbookpubtxt)
val bookdept=view.findViewById<TextView>(R.id.recbookdepttxt)
val bookimage=view.findViewById<ImageView>(R.id.recbookimg)
fun bind(book:Booksmodel,clicklistner:OnItemClicklistner){
bookname.text=book.BookName
bookpublication.text=book.BookPublication
bookdept.text=book.Department
Picasso.get().load(book.BookImage).into(bookimage)
itemView.setOnClickListener{
clicklistner.onItemclick(book)
}
}
}
interface OnItemClicklistner{
fun onItemclick(books:Booksmodel)
}
}
books.kt
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.fragmentmenu, menu)
val searchItem = menu.findItem(R.id.search)
val searchView:SearchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
filter(newText)
return false
}
})
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id=item.itemId
when (id) {
R.id.search -> {
return true
}
R.id.sort->{
Toast.makeText(context,"sort",Toast.LENGTH_SHORT).show()
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
private fun filter(text: String) {
val filteredlist = ArrayList<Booksmodel>()
for (item in booklist) {
if (item.BookName!!.lowercase(Locale.getDefault())
.contains(text.lowercase(Locale.getDefault()))
) {
filteredlist.add(item)
}
}
if (filteredlist.isEmpty()) {
Toast.makeText(context, "No Data Found..", Toast.LENGTH_SHORT).show()
} else {
bookadapter.filterlist(filteredlist)
}
}
If coloring the searched text with different color is enough highlighting for you, try following:
Modify your bookadapter class:
class bookadapter() : ... {
var searchText: String = ""
...
// update your adapter about the current search text as well
fun filterList(filterList: List<BookModel>, searchText: String) {
this.searchText = searchText
booklist = filterList
notifyDataSetChanged()
}
}
Then, modify your bookholder to highlight part of the text that matches searchText:
inner class bookholder(view: View) : RecyclerView.ViewHolder(view) {
private val bookNameTextView: TextView = view.findViewById(R.id.tv_book_name)
fun bind(book: BookModel, clickListener: OnItemClickListener) {
if (searchText.isNotBlank()) {
val highlightedText = book.bookName.replace(searchText, "<font color='red'>$searchText</font>", true)
bookNameTextView.text = HtmlCompat.fromHtml(highlightedText, HtmlCompat.FROM_HTML_MODE_LEGACY)
} else {
bookNameTextView.text = book.bookName
}
...
itemView.setOnClickListener {
clickListener.onItemClick(book)
}
}
}
Finally, in your activity/fragment's private fun filter(text: String) function, you need to pass the filtered text to the adapter:
bookadapter.filterlist(filteredlist, text)
I made a working demo reflecting your use case (simplified), check out the demo if you are still unsure. The result looks like follow: https://imgur.com/a/QYEijyR

How to use Filterable in ListAdapter using Kotlin?

I would use a SearchView to filter my RecyclerView, on stackoverflow and other sites i've found just examples of using Filterable with Java and with RecyclerView.Adapter while i'm using ListAdapter..
So i was trying to make the custom filter by my self but when i try to filter the adapter i just get a null on my MutableList in publishResults.
My Adapter code looks like this:
class ArticoliListAdapter : ListAdapter<Articolo, ArticoliListAdapter.ArticoliViewHolder>(ArticoliComparator()), Filterable {
private val list = mutableListOf<Articolo>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticoliViewHolder {
return ArticoliViewHolder.create(parent)
}
override fun onBindViewHolder(holder: ArticoliViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current)
}
override fun getItemId(position: Int): Long {
val articolo = currentList[position]
return articolo.barcode.hashCode().toLong()
}
class ArticoliViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val barcode: TextView = itemView.findViewById(R.id.barcode)
private val qta: TextView = itemView.findViewById(R.id.qta)
private val desc: TextView = itemView.findViewById(R.id.desc)
private val um: TextView = itemView.findViewById(R.id.um)
fun bind(articolo: Articolo?) {
barcode.text = articolo?.barcode
qta.text = articolo?.qta?.formatForQta()
um.text = articolo?.um?.toLowerCase(Locale.ITALIAN)
desc.text = if(articolo?.desc.isNullOrEmpty()) "-" else articolo?.desc
}
private fun Float.formatForQta(): String {
val floatString = this.toString()
val decimalString: String = floatString.substring(floatString.indexOf('.') + 1, floatString.length)
return when (decimalString.toInt() == 0) {
true -> this.toInt().toString()
false -> "%.3f".format(this)
}
}
companion object {
fun create(parent: ViewGroup): ArticoliViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_layout, parent, false)
return ArticoliViewHolder(view)
}
}
}
class ArticoliComparator : DiffUtil.ItemCallback<Articolo>() {
override fun areItemsTheSame(oldItem: Articolo, newItem: Articolo): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Articolo, newItem: Articolo): Boolean {
return oldItem.qta == newItem.qta
}
}
override fun getFilter(): Filter {
return customFilter
}
private val customFilter = object: Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = mutableListOf<Articolo>()
if (constraint == null || constraint.isEmpty()){
filteredList.addAll(currentList)
}else {
val filterPattern = constraint.toString().toLowerCase(Locale.ITALIAN).trim { it <= ' ' }
for (item in currentList) {
if (item.barcode.toLowerCase(Locale.ITALIAN).contains(filterPattern) || item.desc?.toLowerCase(
Locale.ITALIAN
)!!.contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
list.clear()
list.addAll(filterResults?.values as MutableList<Articolo>)
notifyDataSetChanged()
}
}
}
So i was wondering which would be the right way to built a custom filter to filter my data in the recyclerView by using ListAdapter in Kotlin.
I'm calling the filter in my fragment like this:
override fun onQueryTextChange(query: String?): Boolean {
adapter.filter.filter(query)
return false
}
But when i try to filter nothing happend and still all items are shown...
Data to the RecyclerView adapter is set from my ViewHolder and the data is get from the DataBase (LiveData<List<Articolo>>)
Here is the code from my Fragment:
articoliViewModel.articoli.observe(viewLifecycleOwner) { articoli ->
articoli.let { adapter.submitList(it) }
}
Few flaws in your code which i am listing down below.
currentList is holding the current items which r on list not the complete list of items . i.e if you have 10 items and after filter u get 3 items then currentList will be holding 3 items not 10 . So you can not use currentList for filtering the list . instead u hold on to the CompleteList and apply filter on this one .
you should not be calling notifyDataSetChanged() this just defeats the whole purpose of having DiffUtils, instead you call #submitList
Al thought you have a reference to complete list as global variable but you have never assigned value to it its always empty.
I have made a working sample to illustrate. pls try same with your code adding the essential code below. I have use type as String just to make sample easy to understand you can use your custom object. You can also modify the code to make it look better but i think its enough to get the idea how ListAdapter works.
class ArticoliListAdapter : ListAdapter<String, ArticoliListAdapter.ArticoliViewHolder>(ArticoliComparator()), Filterable {
private var list = mutableListOf<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticoliViewHolder {
return ArticoliViewHolder.create(parent)
}
override fun onBindViewHolder(holder: ArticoliViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current)
}
fun setData(list: MutableList<String>?){
this.list = list!!
submitList(list)
}
class ArticoliViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val desc: TextView = itemView.findViewById(R.id.txtName)
fun bind(name: String) {
desc.text = name.toUpperCase()
}
companion object {
fun create(parent: ViewGroup): ArticoliViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_list, parent, false)
return ArticoliViewHolder(view)
}
}
}
class ArticoliComparator : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
}
override fun getFilter(): Filter {
return customFilter
}
private val customFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = mutableListOf<String>()
if (constraint == null || constraint.isEmpty()) {
filteredList.addAll(list)
} else {
for (item in list) {
if (item.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
submitList(filterResults?.values as MutableList<String>)
}
}
}
When you set data to adapter you call setData not submitList.
articoliViewModel.articoli.observe(viewLifecycleOwner) { articoli ->
articoli.let { adapter.setData(it) }
}
Correct my if I'm wrong, but I would say that there's a mistake here:
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
list.clear()
list.addAll(filterResults?.values as MutableList<Articolo>)
notifyDataSetChanged()
}
I would do the following:
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
list.clear()
list.addAll(filterResults?.values as MutableList<Articolo>)
submitList(list)
}

how implement searchview with recyclerview correctly?

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

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?

Categories

Resources