Android Recyclerview Remove Animation Is Applied To All Items Strange Behaviour - android

I am using following library to animate items in my recyclerview on removal
https://github.com/wasabeef/recyclerview-animators
I am trying to apply slide to right animation to only removed item but for some reason all items in recyclerview disappears first and then animated back to their position strangely.
Please watch the following video.
Here is my code its very simple:
friends_screen_friendlist_recyclerview.itemAnimator = SlideInRightAnimator()
friends_screen_friendlist_recyclerview.itemAnimator?.apply {
addDuration = 120;
removeDuration = 1000;
moveDuration = 500;
changeDuration = 500;
}
friendItems.removeAt(0)
friends_screen_friendlist_recyclerview.adapter!!.notifyItemRemoved(0)
Adapter class:
class FriendListAdapter(val items : ArrayList<FriendListItem>, val context: Context,val listener:FriendListListener) : androidx.recyclerview.widget.RecyclerView.Adapter<FriendListViewHolder>(),Filterable {
var filterItems = ArrayList<FriendListItem>()
init{
filterItems = items
}
// Gets the number of animals in the list
override fun getItemCount(): Int {
return filterItems.size
}
override fun getItemViewType(position: Int): Int {
return position
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charSearch = constraint.toString()
if (charSearch.isEmpty()) {
filterItems = items
} else {
val resultList = ArrayList<FriendListItem>()
for (row in items) {
if (row.userName.toLowerCase(Locale.ROOT).contains(charSearch.toLowerCase(Locale.ROOT))) {
resultList.add(row)
}
}
filterItems = resultList
}
val filterResults = FilterResults()
filterResults.values = filterItems
return filterResults
}
#Suppress("UNCHECKED_CAST")
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
filterItems = results?.values as ArrayList<FriendListItem>
notifyDataSetChanged()
}
}
}
// Inflates the item views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FriendListViewHolder {
return FriendListViewHolder(LayoutInflater.from(context).inflate(R.layout.friendlist_item, parent, false))
}
// Binds each animal in the ArrayList to a view
override fun onBindViewHolder(holder: FriendListViewHolder, position: Int) {
//holder?.profileImage?.background = items[position].profileImage
holder?.userName?.text = filterItems[position].userName
holder?.level?.text = "Seviye "+filterItems[position].level
// do the necessary profilePic checks.
Picasso.get().load(filterItems[position].profileImage).into(holder?.profileImage, object : Callback {
override fun onSuccess() {
listener.onProfilePicLoaded()
}
override fun onError(e: Exception?) {
listener.onProfilePicLoaded() // error but anyway. just tell messages class its loaded so it can show the page.
}
})
if(filterItems[position].gender == 0){
holder?.gender?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.male_icon))
}else{
holder?.gender?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.female_icon))
}
if(filterItems[position].isOnline){
holder?.isOnline?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.online_circle))
}else{
holder?.isOnline?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.offline_circle))
}
holder.itemContainer.setOnClickListener {
listener.onFriendSelected(filterItems[position])
}
holder.itemContainer.setOnLongClickListener {
listener.onFriendSelectedAndHolded(filterItems[position],position)
Log.d("yunusdebug","long")
return#setOnLongClickListener true
}
}
}
class FriendListViewHolder (view: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each animal to
val itemContainer = view.friendlist_item_container
val userName = view.friendlist_item_username_textview
val level = view.friendlist_item_level_textview
val isOnline = view.friendlist_item_isOnline
val profileImage = view.friendlist_item_profile_image
val gender = view.friendlist_item_gender
}

When you remove an item in your adapter you should also notify the new range. Please try the following:
friendItems.removeAt(index)
notifyItemRemoved(index)
notifyItemRangeChanged(index,friendItems.size)

Related

Why searchview for recyclerview is not working?

I did my adapter and made a function that would filter my list. But the issue is that it's no showing the results and i don't know why. There 2 lists: one of them is full list and the other one is the filtered list. Here is my adapter:
class SearchAdapter : RecyclerView.Adapter<SearchViewHolder>(), Filterable {
var fullInstitutionData: ArrayList<InstitutionModel> = ArrayList()
var institutionData: ArrayList<InstitutionModel> = ArrayList()
init {
institutionData = ArrayList()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val inflater = LayoutInflater.from(parent.context)
return SearchViewHolder(inflater, parent)
}
override fun getItemCount(): Int = institutionData.size
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
val dialogData: InstitutionModel = institutionData[position]
holder.bind(dialogData)
}
override fun getFilter(): Filter {
return customFilter
}
fun addDataFromClient(listDataClient: ArrayList<InstitutionModel>) {
institutionData.addAll(listDataClient)
fullInstitutionData = ArrayList(listDataClient)
notifyDataSetChanged()
}
private val customFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = arrayListOf<InstitutionModel>()
if (constraint == null || constraint.length == 0) {
filteredList.addAll(institutionData)
} else {
val filterPattern = constraint.toString().toLowerCase().trim()
for (item in fullInstitutionData) {
if (item.name.toLowerCase().contains(filterPattern) || item.name?.toLowerCase()!!
.contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
institutionData.clear()
institutionData.addAll(filterResults?.values as MutableList<InstitutionModel>)
}
}
}
And this is my search function that i did the setOnQueryListener in the my fragment:
fun searchInstitution() {
searchViewInstitutions.apply {
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
searchAdapter.filter.filter(newText)
return false
}
})
}
}
This is the code when i call the recyclerview on onActivityCreated:
val llm = LinearLayoutManager(context)
llm.orientation = LinearLayoutManager.VERTICAL
rvSearch.layoutManager = llm
rvSearch.adapter = SearchAdapter()
searchAdapter.addDataFromClient(data)
searchInstitution()
There are two fixes needed:
notifyDataSetChanged() when you publishResults()
override fun publishResults(constraint: CharSequence?, filterResults: FilterResults?) {
institutionData.clear()
institutionData.addAll(filterResults?.values as MutableList<InstitutionModel>)
notifyDataSetChanged() // <<< Change Here
}
Add the full list when there is no match instead of the filtered one
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = arrayListOf<InstitutionModel>()
if (constraint == null || constraint.length == 0) {
filteredList.addAll(fullInstitutionData) // <<< Change Here

Recyclerview Filter not working in kotlin android

I trying filtering recycler view with filter function in adapter but it's not working.
I have added searchview listener like this :
init(){
binding.svCategory.setOnQueryTextListener(this)
}
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
adapterCategories.getFilter().filter(newText)
return true
}
and here is my adapter that i have created :
class CategoriesAdapter(
var context: Context,
var listCategories: ArrayList<Categories>,
var onGalleryClick: (Any) -> Unit,
var onCameraClick: (Any) -> Unit,
var onDeleteClick: (Any) -> Unit
) :
RecyclerView.Adapter<CategoriesAdapter.CategoriesViewHolder>(), Filterable {
private var filterList: ArrayList<Categories> = listCategories
init {
filterList = listCategories
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): CategoriesViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.rv_category_item, viewGroup, false)
return CategoriesViewHolder(view)
}
override fun onBindViewHolder(holder: CategoriesViewHolder, position: Int) {
holder.tvCategoryName.text = listCategories[position].name
if (listCategories[position].imageUri != null) {
Glide
.with(context)
.load(Uri.parse(listCategories[position].imageUri))
.centerCrop()
.placeholder(R.drawable.add)
.into(holder.ivImage)
}
if (listCategories[position].imageUri.isNullOrEmpty()) {
holder.ivDelete.visibility = View.GONE
} else {
holder.ivDelete.visibility = View.VISIBLE
}
holder.ivGallery.setOnClickListener {
onGalleryClick(position)
}
holder.ivCamera.setOnClickListener {
onCameraClick(position)
}
holder.ivDelete.setOnClickListener {
onDeleteClick(position)
}
}
override fun getItemCount(): Int {
return filterList.size
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charSearch = constraint.toString()
filterList = if (charSearch.isEmpty()) {
listCategories
} else {
val resultList = ArrayList<Categories>()
for (row in listCategories) {
if (row.name.toLowerCase().contains(constraint.toString().toLowerCase())) {
resultList.add(row)
}
}
resultList
}
val filterResults = FilterResults()
filterResults.values = filterList
return filterResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
filterList = results?.values as ArrayList<Categories>
notifyDataSetChanged()
}
}
}
class CategoriesViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var tvCategoryName: AppCompatTextView = view.findViewById(R.id.tvCategoryName)
var ivImage: AppCompatImageView = view.findViewById(R.id.ivImage)
var ivGallery: AppCompatImageView = view.findViewById(R.id.ivGallery)
var ivCamera: AppCompatImageView = view.findViewById(R.id.ivCamera)
var ivDelete: AppCompatImageView = view.findViewById(R.id.ivDelete)
}
}
but it doesn't filter item properly, it shows the right count but doesn't show proper items in the recyclerview,
So what to do for this issue? Can anyone help?
Thanks in advance.
inside onBindViewHolder you are using listCategories[position] instead of filterList[position]. on start it have same content like original listCategories (init{} call), but after filtering getItemCount returns size of filtered results, but in onBindViewHolder still drawing listCategories array. work only on filterList and keep listCategories untouched, usable only on start (filling filterList) and when restoring original list (e.g. when query removed)
PS. work only on some temporary array inside performFiltering, currently you are changing filterList strictly in line filterList = if. just add var before filterList. this will make this array only locally available, it will be attached to filterResults.values and then updated in publishResults (these functions are called in different Threads)

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

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

Android Recyclerview Filtering Inconsistency detected. Invalid view holder adapter positionViewHolder error

I am trying to filter my recyclerview with a searchbar. Whenever i call myAdapter.filter.filter(newText) from my searchbars onQueryTextChange i get this error:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid
view holder adapter positionViewHolder{8bf63e2 position=6 id=-1,
oldPos=-1, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}
androidx.recyclerview.widget.RecyclerView
Adapter class:
class FriendListAdapter(val items : ArrayList<FriendListItem>, val context: Context) : androidx.recyclerview.widget.RecyclerView.Adapter<FriendListViewHolder>(),Filterable {
var filterItems = ArrayList<FriendListItem>()
init{
filterItems = items
}
override fun getItemCount(): Int {
return filterItems.size
}
override fun getItemViewType(position: Int): Int {
return position
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charSearch = constraint.toString()
if (charSearch.isEmpty()) {
filterItems = items
} else {
val resultList = ArrayList<FriendListItem>()
for (row in items) {
if (row.userName.toLowerCase(Locale.ROOT).contains(charSearch.toLowerCase(Locale.ROOT))) {
resultList.add(row)
}
}
filterItems = resultList
}
val filterResults = FilterResults()
filterResults.values = filterItems
return filterResults
}
#Suppress("UNCHECKED_CAST")
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
filterItems = results?.values as ArrayList<FriendListItem>
notifyDataSetChanged()
}
}
}
// Inflates the item views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FriendListViewHolder {
return FriendListViewHolder(LayoutInflater.from(context).inflate(R.layout.friendlist_item, parent, false))
}
override fun onBindViewHolder(holder: FriendListViewHolder, position: Int) {
//holder?.profileImage?.background = items[position].profileImage
holder?.userName?.text = filterItems[position].userName
holder?.level?.text = "Seviye "+filterItems[position].level
if(filterItems[position].gender == 0){
holder?.gender?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.male_icon))
}else{
holder?.gender?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.female_icon))
}
if(filterItems[position].isOnline){
holder?.isOnline?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.online_circle))
}else{
holder?.isOnline?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.offline_circle))
}
}
}
class FriendListViewHolder (view: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each animal to
val itemContainer = view.friendlist_item_container
val userName = view.friendlist_item_username_textview
val level = view.friendlist_item_level_textview
val isOnline = view.friendlist_item_isOnline
val profileImage = view.friendlist_item_profile_image
val gender = view.friendlist_item_gender
}
without implementing filtering and filterItems list my recyclerview is working fine. must be something wrong on filtering part, or the way class FriendListViewHolder implemented causesthis

Categories

Resources