'android Change background color of odd rows in RecyclerView when using DiffUtil
I have a RecyclerView and Every odd row has a different background color.
I try to add DiffUtil to speed up the updates like this:
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.asdf.android.R
import com.asdf.android.network.model.trade.MarketTradeItem
import kotlinx.android.synthetic.main.row_trade_history.view.*
class MarketTradesListAdapter(
private val context: Context,
private var list: MutableList<MarketTradeItem>,
val theme: Int
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflate =
LayoutInflater.from(parent.context).inflate(R.layout.row_trade_history, parent, false)
return ItemHolder(inflate)
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val itemHolder = holder as ItemHolder
itemHolder.bind(list[position], position)
}
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
val payload = payloads[0] as DiffUtilPayload
val itemHolder = holder as ItemHolder
itemHolder.bind(list[position], position, payload)
}
}
fun setList(it: List<MarketTradeItem>) {
it.forEachIndexed { index, item -> item.position = index }
DiffUtil.calculateDiff(DiffCallback(list, it),true).dispatchUpdatesTo(this)
list.clear()
list.addAll(it)
}
inner class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(
order: MarketTradeItem,
position: Int,
payload: DiffUtilPayload = DiffUtilPayload()
) {
var color: Int
if (theme == R.style.Apptheme) {
color = R.color.listBackgroundColorLight
} else {
color = R.color.listBackgroundColorDark
}
// if (payload.isPositionChanged)
itemView.setBackgroundColor(
if (position % 2 == 0) ContextCompat.getColor(
itemView.context,
android.R.color.transparent
) else
ContextCompat.getColor(itemView.context, color)
)
itemView.textViewPrice.setTextColor(
ContextCompat.getColor(
itemView.context,
if (order.isRaise) R.color.buy_green else R.color.sell_red
)
)
if (payload.isPriceChanged) {
itemView.textViewPrice.text = order.price
}
if (payload.isAmountChanged) {
itemView.textViewAmount.text = order.amount
}
if (payload.isTimeChanged) {
itemView.textViewTime.text = order.time
}
}
}
class DiffCallback(
private val oldList: List<MarketTradeItem>,
private val newList: List<MarketTradeItem>
) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldList[oldItemPosition]
val newItem = newList[oldItemPosition]
return DiffUtilPayload(
oldItem.price != newItem.price,
oldItem.amount != newItem.amount,
oldItem.createdAt != newItem.createdAt,
oldItem.time != newItem.time,
oldItem.position != newItem.position
)
}
}
data class DiffUtilPayload(
val isPriceChanged: Boolean = true,
val isAmountChanged: Boolean = true,
val isCreatedAtChanged: Boolean = true,
val isTimeChanged: Boolean = true,
val isPositionChanged: Boolean = true
)
}
The problem is that the background color of even and odd rows does not appear correctly when new items are inserted to the list and appears like this:
The background color is set randomly and not appear as alternate even/odd.
What can I do to fix this?
Solution 1:[1]
I think you need to change areContentsTheSame method in your DiffUtil. If the contents are same, it won't calculate the change which won't dispatch notifyItemChanged(position) to the adapter. And it won't call onBindViewHolder for that position/item.
You should try changing the method to
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition] && oldItemPosition % 2 == newItemPosition % 2
}
Solution 2:[2]
if items are added at the beginning. the fast solution would be, in onBindViewHolder
itemHolder.bind(list[position], position, payload)
change to:
itemHolder.bind(list[position], getItemCount() - position, payload)
and itemHolder.bind(list[position], position)
change to:
itemHolder.bind(list[position], getItemCount() - position)
I think the view holder should not know the postion. so I suggest you some refactoring.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Froyo |
| Solution 2 | Aleksadnre Bibilashvili |
