После обновления recyclerview слетает цвет у выбраного item

Пишу приложение ToDo List. Все таски реализованы в виде элементов RecyclerView. Пользователь через контекстное меню может выбирать цвет для определенного таска. Обновление RecyclerView произвожу с помощью SwipeRefreshLayout, после которого цвет выбранного таска исчезает. Сами цвета хранятся в SharedPreferences.

Код TaskViewHolder:

  inner class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
        View.OnClickListener, PopupMenu.OnMenuItemClickListener {
        private val taskDescription: TextView = itemView.taskDescription
        private val cardView: CardView = itemView.task_list_item

        private var checkedItem: Int = -1

        init {
            cardView.setOnClickListener(this)
        }

        fun bind(description: String) {
            taskDescription.text = description
        }

        fun bindColorToView(position: Int) {
            if (checkedItem == position) {
                val colorPreferences =
                    context.getSharedPreferences("Color Settings", Context.MODE_PRIVATE)
                cardView.setCardBackgroundColor(colorPreferences.getInt("Color code", Color.WHITE))
            }
        }

        override fun onClick(view: View?) {
            showPopupMenu(view)
        }

        private fun showPopupMenu(view: View?) {
            val popupMenu = PopupMenu(view?.context, view)
            popupMenu.inflate(R.menu.context_menu)
            popupMenu.setOnMenuItemClickListener(this)
            popupMenu.show()
        }

        override fun onMenuItemClick(menuItem: MenuItem?): Boolean {
            return when (menuItem?.itemId) {
                R.id.importance_Low -> {
                    cardView.setCardBackgroundColor(Color.GREEN)
                    val colorPreference =
                        context.getSharedPreferences("Color Settings", Context.MODE_PRIVATE)
                    colorPreference.edit().putInt("Color code", Color.GREEN).apply()
                    true
                }
                R.id.importance_Medium -> {
                    cardView.setCardBackgroundColor(Color.YELLOW)
                    val colorPreference =
                        context.getSharedPreferences("Color Settings", Context.MODE_PRIVATE)
                    colorPreference.edit().putInt("Color code", Color.YELLOW).apply()
                    true
                }
                R.id.importance_High -> {
                    cardView.setCardBackgroundColor(Color.RED)
                    val colorPreference =
                        context.getSharedPreferences("Color Settings", Context.MODE_PRIVATE)
                    colorPreference.edit().putInt("Color code", Color.RED).apply()
                    true
                }
                else -> false
            }
        }
    }

onBindViewHolder метод:

override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
        content[position].description?.let { holder.bind(it) }
        
        holder.bindColorToView(position)
    }

Метод обновления RecyclerView:

private fun refreshData() {
        refresher.isRefreshing = true
        contentViewModel.getTaskList(applicationContext)
        contentViewModel.tasks.observe(this, Observer {
            contentAdapter = TaskAdapter(it, applicationContext)
            taskList.adapter = contentAdapter
            contentAdapter.notifyDataSetChanged()
        })
        refresher.isRefreshing = false
    }

Ответы (2 шт):

Автор решения: Sergei Buvaka
  • Я не вижу в вашем коде где вы меняете значние checkedItem. Смею предположить, что проблема в том, что после апдейта вашего Adapter-а это значние теряется.

  • Еще проблема может крыться здесь:

    fun bindColorToView(position: Int) {
              if (checkedItem == position) {
                  val colorPreferences =
                      context.getSharedPreferences("Color Settings", Context.MODE_PRIVATE)
                  cardView.setCardBackgroundColor(colorPreferences.getInt("Color code", Color.WHITE))
              }
          }
    

    RecyclerView реализует паттерн ViewHolder, а значит, что он переиспользует View. Поэтому для RecyclerView.Adapter есть негласное правило: "Если в ViewHolder-е есть. if то должен быть и else т.к. вы можете получить баг когда придет переиспользованная View она не попадет по if и там останется прошлое значние.

  • А вообще как это делается по хорошему: В Presenter/ViewModel генерится список с готовыми моделями, любые изменения (в том числе и цвет) должны изменяться в этом списке. А потом просто Adapter должен заполниться нужными значениями.

→ Ссылка
Автор решения: Alex Filonenko

Решение: Я сохранил в SharedPreferences позицию RecyclerView Item что дало в дальнейшем возможность обновлять сам RecyclerView с сохранением цвета у вібраного таска

→ Ссылка