Recyclerview items are changing when scrolling in mutliviewtype scenarios

38 Views Asked by At

Hi I'm using recyclerview for handling multiviewType of the lists, but when i scroll the cells are changing sometimes. I referred many SO's answer but it didnt worked. Override the getitemid as well but didnt worked need help for the following code.

Here is my code of my adapter:-

class Adapter(val onItemClick: (Data) -> Unit) :
BaseRecyclerViewAdapter<Data, BaseRecyclerViewHolder<Data>>() {

companion object {
    const val VIEW_LINK = 1
    const val VIEW_POST = 2
    const val VIEW_LOADER = 3
}

override fun createItemViewHolder(
    parent: ViewGroup,
    viewType: Int
): BaseRecyclerViewHolder<Data> {
    val layoutInflater = LayoutInflater.from(parent.context)
    when (viewType) {
        VIEW_LINK -> {
            return LinkViewHolder(
                LayoutLinkPreviewBinding.inflate(
                    layoutInflater,
                    parent,
                    false
                )
            )
        }

        VIEW_POST -> {
            return PostViewHolder(
                LayoutPostPreviewBinding.inflate(
                    layoutInflater,
                    parent,
                    false
                )
            )
        }

        else -> {
            return LoadMoreViewHolder(
                ItemProgressLoadMoreBinding.inflate(
                    layoutInflater,
                    parent,
                    false
                ).root
            )
        }
    }
}

override fun bindItemViewHolder(holder: BaseRecyclerViewHolder<Data>, position: Int) {
    when (holder.itemViewType) {
        VIEW_LINK -> {
            items.let {
                (holder as LinkViewHolder).bind(it[position])
            }
        }

        VIEW_POST -> {
            items.let {
                (holder as PostViewHolder).bind(it[position])
            }
        }

        VIEW_LOADER -> {
            (holder as LoadMoreViewHolder).pdLoadMore.show()

        }

    }

}

override fun getItemViewType(position: Int): Int {
    return when (items[position].type) {
        "link" -> {
            VIEW_LINK
        }

        "post" -> {
            VIEW_POST
        }

        "media" -> {
            VIEW_POST
        }

        else -> {
            VIEW_LOADER
        }
    }
}

inner class LinkViewHolder(private val layoutLinkPreviewBinding: LayoutLinkPreviewBinding) :
    BaseRecyclerViewHolder<Data>(layoutLinkPreviewBinding.root) {
    private var coroutineScope: CoroutineScope? = null
    var link: String? = null


    override fun bind(item: Data) {
        ensureCoroutineScope()
        fetchLinkPreview(item)
    }

}

inner class PostViewHolder(private val postPreviewBinding: LayoutPostPreviewBinding) :
    BaseRecyclerViewHolder<Data>(postPreviewBinding.root) {

    override fun bind(item: Data) {

        setUpUI(item)
    }
}

inner class LoadMoreViewHolder(itemView: View) :
    BaseRecyclerViewHolder<Data>(itemView) {
    var pdLoadMore: loaderView

    init {
        pdLoadMore = itemView.findViewById(R.id.pdLoadMore)
    }

    override fun bind(item: ShowCaseData) {}
}

override fun getItemId(position: Int): Long {
    return items[position].hashCode().toLong()
}

}

base recycler:-

abstract class BaseRecyclerViewAdapter<S, T : RecyclerView.ViewHolder> : RecyclerView.Adapter<T>() {

/**
 * list of all items
 */
private var _items = mutableListOf<S>()

var items: List<S> = _items
    private set

var isLoadingAdded = false

/**
 * create view holder of recycler view item
 */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): T {
    return createItemViewHolder(parent, viewType)
}

/**
 * bind recycler view item with data
 */
override fun onBindViewHolder(holder: T, position: Int) {
    bindItemViewHolder(holder, position)
}

/*
 * If the payloads list is not empty,the ViewHolder is currently bound to old data and Adapter may run an efficient partial
 * update using the payload info.  If the payload is empty,  Adapter must run a full bind.
 */
override fun onBindViewHolder(holder: T, position: Int, payloads: MutableList<Any>) {
    if (payloads.isNotEmpty()) {
        bindItemViewHolderPayload(holder, position, payloads)
    } else {
        bindItemViewHolder(holder, position)
    }
}

/**
 * get count for visible count
 */
override fun getItemCount() = _items.size

/**
 * abstract method to create custom view holder
 */
protected abstract fun createItemViewHolder(
    parent: ViewGroup,
    viewType: Int
): T

/**
 * abstract method to bind custom data
 */
protected abstract fun bindItemViewHolder(holder: T, position: Int)
/**
 * abstract method to bind custom data using payloads
 */
protected open fun bindItemViewHolderPayload(holder: T, position: Int,payloads: MutableList<Any>){}

/**
 * add all items to list
 */
fun addAll(items: List<S>, clearPreviousItems: Boolean = false) {
    if (clearPreviousItems) {
        this._items.clear()
    }
    this._items.addAll(items)
    notifyDataSetChanged()
}

/**
 * add all items to list with notifyrangechanged
 */
fun addAll(items: List<S>, clearPreviousItems: Boolean = false , existingSize :Int) {
    if (clearPreviousItems) {
        this._items.clear()
    }
    this._items.addAll(items)
    notifyItemRangeInserted(existingSize,items.size)
}

/**
 * add item (at position - optional)
 */
fun addItem(item: S, position: Int = _items.size, clearPreviousItems: Boolean = false) {
    var adapterPosition = position
    if (clearPreviousItems) {
        this._items.clear()
        adapterPosition = 0 // set position to 0 after items arrayList gets clear
    }
    this._items.add(adapterPosition, item)
    notifyItemInserted(adapterPosition)
}

/**
 * remove item from position
 */
fun removeItem(position: Int) {
    if (position != -1) {
        _items.removeAt(position)
        notifyItemRemoved(position)
    }
}

/**
 * get item at position
 */
fun getItemAt(position: Int): S {
    return _items[position]
}

/**
 * update item at position
 */
fun updateItemAt(position: Int, item: S) {
    if (position != -1) {
        _items[position] = item
        notifyItemChanged(position)
    }
}
/**
 * update item at position with payload
 */
fun updateItemAtWithPayload(position: Int, item: S,  payload:Any?) {
    if (position != -1) {
        _items[position] = item
        notifyItemChanged(position , payload)
    }
}
/**
 * To clear the adapter
 */
fun clearAdapter() {
    _items.clear()
    notifyDataSetChanged()
}

fun showLoadMore(loadMoreItem: S) {
    if (!isLoadingAdded) {
        isLoadingAdded = true
        addItem(loadMoreItem)
    }
}

fun hideLoadMore() {
    if (isLoadingAdded) {
        isLoadingAdded = false
        removeItem(itemCount - 1)
    }
}

fun isEmpty(): Boolean {
    return _items.size == 0
}

}

please help Thanks in advance

1

There are 1 best solutions below

6
Raghul Thavamani On

Override a getItemViewType in your adapter class, By default it looks like,

override fun getItemViewType(position: Int): Int {
    return super.getItemViewType(position)
}

Change that into below,

override fun getItemViewType(position: Int): Int {
    return position
}

This will return the current position of the itemview.