Как сделать список RecyclerView с раскрывающимися карточками, содержащими дочерние элементы с использованием ViewType?

написал вот такую реализацию списка с раскрывающимися карточками. Внутри каждой карточки свой список. Реализовано это с помощью двух ViewType. Первый для заголовка секции, второй для элемента списка. Но при раскрытии элементы списка перемешиваются. Элементы из первой секции перемещаются во вторую, а элементы из третьей секции в первую. Не знаю как исправить. Мне нужно чтобы по тапу на нужную секцию она раскрывалась, а по тапу на следующую секцию, ее элементы не попадали в предыдущую. Ниже будет код который я написал сам. Так как не смог найти никакой подобной реализации в Google. Важно: Библиотеки сторонние использовать нельзя!

Здесь происходит генерация элементов для списка.

object DataGenerator {
val items = mutableListOf<ListItem>()
val first = mutableListOf<TextItem>()
val second = mutableListOf<TextItem>()
val last = mutableListOf<TextItem>()

init {

    for (i in 1..2) {
        first.add(TextItem("Only Blue title", "Content 1"))
    }

    for (i in 1..2) {
        second.add(TextItem("Only Red title", "Lorem ipsum dolor sit ament"))
    }

    for (i in 1..2) {
        last.add(TextItem("Title 2", "Content 2"))
    }
    items.add(HeaderItem("Header item 1", first, false))
    items.add(HeaderItem("Header item 2", second, false))
    items.add(HeaderItem("Header item 3",  last, false))

}

}

Интерфейс ListItem является общим для обоих типов элементов.

interface ListItem

HeaderItem - заголовок секции, реализует ListItem

data class HeaderItem(
val title: String,
val items: List<TextItem>,
var expanded: Boolean = false

): ListItem

Далее идет TextItem, который также реализует ListItem и является элементом дочернего списка.

data class TextItem(
val title: String,
val content: String,

): ListItem

А также имеется класс адаптера, в методе onBindViewHolder которого я написал код, добавления и удаления элементов секции по тапу на заголовок. Вот с ним как раз и проблема.

class SectionedAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items = mutableListOf<ListItem>()

fun setItems(items: MutableList<ListItem>) {
    this.items = items
    notifyDataSetChanged()
}

class HeaderItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
     fun bind(item: HeaderItem) = with(itemView) {
        textViewHeader.text = item.title
    }
}
class TextItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
     fun bind(item: TextItem) = with(itemView) {
         textViewTitle.text = item.title
         textViewContent.text = item.content
    }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    return when(viewType) {
        TYPE_HEADER -> {
            val headerView = LayoutInflater.from(parent.context).inflate(R.layout.item_header, parent, false)
            return HeaderItemViewHolder(headerView)
        }
        TYPE_TEXT -> {
            val textView = LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
            return TextItemViewHolder(textView)
        }
        else -> throw IllegalArgumentException()
    }
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val item = items[position]
    when(holder) {
        is HeaderItemViewHolder -> {
            holder.bind(item as HeaderItem)
            holder.itemView.setOnClickListener {
                if(item.expanded) {
                    items.addAll(position + 1, item.items)
                    notifyItemRangeInserted(position + 1, item.items.size)
                }
                else {
                    items.removeAll(item.items)
                    notifyItemRangeRemoved(position - 1, item.items.size)
                }
                item.expanded = !item.expanded

            }
        }
        is TextItemViewHolder -> {
            holder.bind(item as TextItem)
        }
        else -> throw IllegalArgumentException()
    }
}

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

override fun getItemViewType(position: Int): Int {
    val item = items[position]
    return when(item) {
        is HeaderItem -> TYPE_HEADER
        is TextItem -> TYPE_TEXT
        else -> throw IllegalArgumentException()
    }
}

companion object {
    const val TYPE_HEADER = 0
    const val TYPE_TEXT = 1
}

}

Буду рад любой помощи. Бьюсь с этим уже третью неделю. Смотрел в сторону библиотек, но у реализации на ViewType производительность выше. Чем если использовать ту же библиотеку Groupie. Не хочу создавать второй вложенный в карточку RecyclerView. Это приведет к лагам и потере кадров. PS уже тестировал этот вариант

Полный код залил на гит, можете скачать, запустить, воспроизвести баг https://gitlab.com/fursa.ilya/recyclerview


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