I am using diff util to improve performance in my recyclerview as opposed to calling notifyDataSetChanged(). The recyclerview has a header with some chips which can reorder the list by aplhabetical order, highester score etc
When I click the chip in the header and the list reorders its effecting my position. For example if I have the ordered by hightest score and score 100% is position 1 (after the header) and I click the chip to reorder in reverse. Score 100% will now be at the bottom of the list and 0% will be at the top. But now I have to scroll all the way back to the top to see the header chips again. I want the list to reorder but I dont want my on screen position to change.
Here is my adapter code:
class DigitalTestsResultsAdapter(
private val interaction: Interaction? = null,
private val dateUtil: DateUtil,
private val theme: ThemeModel?,
private val username: String?
) : ListAdapter<ResultResponseModel, RecyclerView.ViewHolder>(ResultsDiffCallBack()) {
private val itemViewTypeHeader: Int = 0
private val itemViewTypeItem: Int = 1
private var filteredList = emptyList<ResultResponseModel>()
private val adapterScope = CoroutineScope(Dispatchers.Default)
class ResultsDiffCallBack : DiffUtil.ItemCallback<ResultResponseModel>() {
override fun areItemsTheSame(
oldItem: ResultResponseModel,
newItem: ResultResponseModel
): Boolean {
return oldItem.certificateUrl == newItem.certificateUrl
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(
oldItem: ResultResponseModel,
newItem: ResultResponseModel
): Boolean {
return oldItem == newItem
}
}
fun filterList(list: List<ResultResponseModel>, type: String) {
adapterScope.launch {
when (type) {
"courseName" -> {
filteredList = list.sortedBy { it.courseName }
}
"isCpd" -> {
filteredList = list.sortedBy { it.courseName }.sortedByDescending { it.isCPD }
}
"organisationName" -> {
filteredList = list.sortedBy { it.organisationName }
}
"roleName" -> {
filteredList = list.sortedBy { it.roleName }
}
"score" -> {
filteredList = list.sortedByDescending { it.score }
}
"submitTime" -> {
filteredList = list.sortedByDescending { it.submitTime }
}
}
withContext(Dispatchers.Main) {
submitList(filteredList)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
itemViewTypeHeader -> {
DigitalTestsResultsHeaderViewHolder(
RvDigitalTestResultsHeaderBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
itemViewTypeItem -> {
DigitalTestsResultsViewHolder(
RvDigitalTestsResultsBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
interaction = interaction
)
}
else -> throw ClassCastException("Unknown viewType $viewType")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is DigitalTestsResultsHeaderViewHolder -> {
holder.bind()
}
is DigitalTestsResultsViewHolder -> {
holder.bind(currentList[position])
}
}
}
override fun getItemViewType(position: Int): Int {
return if (position == 0) {
itemViewTypeHeader
} else {
itemViewTypeItem
}
}
override fun getItemCount(): Int {
return if (!currentList.isNullOrEmpty()) {
currentList.size
} else 0
}
inner class DigitalTestsResultsHeaderViewHolder
constructor(
private val binding: RvDigitalTestResultsHeaderBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind() {
with(binding) {
with(theme) {
userName.text = itemView.context.getString(R.string.hi_username, username)
userName.setTextColourHex(this?.textModel?.primaryColor)
chipCv.setCardBackgroundColourHex(this?.interfaceModel?.secondaryColor)
testsChipGroup.setOnCheckedChangeListener { _, checkedId ->
when (checkedId) {
R.id.chipCertified -> {
chipCertified.isChecked = true
filterList(currentList, "isCpd")
}
R.id.chipCourse -> {
chipCourse.isChecked = true
filterList(currentList, "courseName")
}
R.id.chipHighestScore -> {
chipHighestScore.isChecked = true
filterList(currentList, "score")
}
R.id.chipRecent -> {
chipRecent.isChecked = true
filterList(currentList, "submitTime")
}
R.id.chipRole -> {
chipRole.isChecked = true
filterList(currentList, "roleName")
}
R.id.chipSchoolName -> {
chipSchoolName.isChecked = true
filterList(currentList, "organisationName")
}
else -> {
}
}
}
}
}
}
}
inner class DigitalTestsResultsViewHolder
constructor(
private val binding: RvDigitalTestsResultsBinding,
private val interaction: Interaction?
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: ResultResponseModel?) {
with(binding) {
with(theme) {
viewCertificateBtn.setOnClickListener {
interaction?.onItemSelected("certificateBtn", absoluteAdapterPosition, item)
}
retakeTestBtn.setOnClickListener {
interaction?.onItemSelected("retakeTestBtn", absoluteAdapterPosition, item)
}
resultsProgressBar.progress = item?.score?.toFloat() ?: 0f
if (isValidHex(item?.roleColour)) {
resultsProgressBar.circleProgressColor = Color.parseColor(item?.roleColour)
resultsProgressBar.pointerColor = Color.parseColor(item?.roleColour)
}
score.text = item?.score.toString() + "%"
title.text = item?.courseName
date.text = dateUtil.formatStringDateToDDMMYYYY(item?.submitTime)
role.text = item?.roleName
schoolName.text = item?.organisationName
title.setTextColourHex(this?.textModel?.primaryColor)
retakeTestBtn.setTextColourHex(this?.textModel?.primaryColor)
mainCv.setCardBackgroundColourHex(this?.interfaceModel?.secondaryColor)
roleCv.setCardBackgroundColourHex(item?.roleColour)
// Check if course is CPD and display CPD icon
if (item?.isCPD == true) cpdLogo.visibility =
View.VISIBLE else cpdLogo.visibility = View.INVISIBLE
}
}
}
}
interface Interaction {
fun onItemSelected(
tag: String,
position: Int,
result: ResultResponseModel?
)
}
}