Как сделать реактивный элемент внутри RecyclerView? - PullRequest
6 голосов
/ 30 мая 2019

Допустим, мы хотим перечислить все загружаемые файлы с прогрессом (это просто пример, чтобы показать проблему).

Каждый элемент может быть представлен этим простым классом:

data class DownloadingFileItem(val url: String, val progress: Int)

Теперь, как вы видите, прогресс является постоянным значением. Всякий раз, когда процесс загрузки изменяется, нам нужно перезагрузить наш RecyclerView или хотя бы один конкретный ViewHolder, который представляет наш элемент. Конечно, это будет довольно простой задачей с notifyItemChanged() или DiffUtil, но давайте усложним задачу и предположим, что повторное связывание может привести к некоторым нежелательным изменениям вида (например, остановка запущенной анимации и т. Д.) Или изменения очень частые. Таким образом, единственным решением было бы сделать наш элемент более реактивным и изменить постоянный прогресс на LiveData или Observable объект. Но как этого добиться?

1 Ответ

1 голос
/ 12 июня 2019

Этого можно добиться, сохранив карту ViewHolder и стабильный идентификатор в своем адаптере. Если вы хотите обновить определенный элемент, используйте этот идентификатор, чтобы найти viewHolder и обновить представление.

data class Item(val id: String, val progress: Int)

ViewHolder, который хранит ссылку на идентификатор, который мы можем использовать, чтобы удалить его с карты после его повторного использования.

class ObservableViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    private val seek = itemView.findViewById<SeekBar>(R.id.seek)
    private val num = itemView.findViewById<TextView>(R.id.num)

    var id: String? = null

    fun bind(item: Item) {
        id = item.id
        num.text = item.id
        seek.progress = item.progress
    }
}

Ваш адаптер

class ObservableAdapter: RecyclerView.Adapter<ObservableViewHolder>() {

    private var items = listOf<Item>()
    private val viewHolderMap = hashMapOf<String, ObservableViewHolder>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ObservableViewHolder {
        return ObservableViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false))
    }

    override fun getItemCount(): Int {
        return items.size
    }

    override fun onBindViewHolder(holder: ObservableViewHolder, position: Int) {
        val item = items[position]
        holder.bind(item)
        viewHolderMap[item.id] = holder
    }

    override fun onViewRecycled(holder: ObservableViewHolder) {
        super.onViewRecycled(holder)
        holder.id?.let { viewHolderMap.remove(it) }
    }

    fun updateItems(items: List<Item>) {
        this.items = items
    }

    fun update(item: Item) {
        viewHolderMap[item.id]?.bind(item)
    }

}

Вот как в моем демонстрационном примере обновляется представление.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val adapter = ObservableAdapter()
        var items = listOf(
            Item("1", 10),
            Item("2", 20),
            Item("3", 30),
            Item("4", 10),
            Item("5", 20),
            Item("6", 30),
            Item("7", 10),
            Item("8", 20),
            Item("9", 30),
            Item("10", 10),
            Item("11", 20),
            Item("12", 30),
            Item("13", 10),
            Item("14", 20),
            Item("15", 30)
        )

        val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = adapter

        adapter.updateItems(items)
        adapter.notifyDataSetChanged()

        findViewById<View>(R.id.cta).setOnClickListener {
            val updated = items[0].copy(progress = items[0].progress + 10)
            adapter.update(updated)
            items = items.toMutableList().apply {
                removeAt(0)
                add(0, updated)
            }
            adapter.updateItems(items)
            // No call to notifyDatasetChanged()

        }
    }
}

Вот как выглядит демо

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...