RecyclerView с несколькими типами представлений с помощью Generics - PullRequest
0 голосов
/ 11 июля 2019

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

Класс BaseViewHolder равен

abstract class BaseViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}

Затем я создал класс держателя вида для определенного типа представления

inner class CarouselViewHolder(itemView: View) : BaseViewHolder<HomeCarousel>(itemView) {
    override fun bind(item: HomeCarousel) {
    }
}

Я получаю сообщение об ошибке при доступе к этой функции связывания из забавного метода onBindViewHolder.

Запроектированный тип «BaseViewHolder <*>» запрещает использование «публичной абстрактной забавной привязки (элемент: T):

 override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
    val element = data[position]
    when (element) {
        is HomeCarousel -> {
            holder.bind(element)
        }
        else -> throw java.lang.IllegalArgumentException("Invalid binding")
    }
}

Получение ошибки на

       holder.bind(element)

Это весь мой класс Адаптера

class HomePageRecylerAdapter(private val data: ArrayList<Any>) : RecyclerView.Adapter<BaseViewHolder<*>>() {

companion object {
    const val typeCarousel = 0
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
    return when (viewType) {
        typeCarousel -> {
            val view =
                LayoutInflater.from(parent.context).inflate(R.layout.recycler_item_home_carasoul, parent, false)
            CarouselViewHolder(view)
        }
        else -> throw IllegalArgumentException("Invalid view type")
    }

}

override fun getItemCount() = data.size

override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
    val element = data[position]
    when (element) {
        is HomeCarousel -> {
            holder.bind(element)
        }
        else -> IllegalArgumentException("Invalid binding")
    }
}



override fun getItemViewType(position: Int): Int {
    val element = data[position]
    return when (element) {
        is HomeCarousel -> typeCarousel
        else -> throw IllegalArgumentException("Invalid type of data {$position}")
    }
}
inner class CarouselViewHolder(itemView: View) : BaseViewHolder<HomeCarousel>(itemView) {
    override fun bind(item: HomeCarousel) {
    }
}
}
abstract class BaseViewHolder<T>(itemView: View) : 
RecyclerView.ViewHolder(itemView) {
   abstract fun bind(item: T)
} 

1 Ответ

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

TLDR;

Хорошо, вот код, который должен работать:

class HomePageRecylerAdapter(private val data: ArrayList<Any>) : RecyclerView.Adapter<BaseViewHolder<Any>>() {

    companion object {
        const val typeCarousel = 0
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<Any> {
        return when (viewType) {
            typeCarousel -> {
                val view =
                        LayoutInflater.from(parent.context).inflate(0, parent, false)
                CarouselViewHolder(view) as BaseViewHolder<Any>
            }
            else -> throw IllegalArgumentException("Invalid view type")
        }

    }

    override fun getItemCount() = data.size

    override fun onBindViewHolder(holder: BaseViewHolder<Any>, position: Int) {
        val element = data[position]
        when (element) {
            is HomeCarousel -> {
                holder.bind(element)
            }
            else -> IllegalArgumentException("Invalid binding")
        }
    }



    override fun getItemViewType(position: Int): Int {
        val element = data[position]
        return when (element) {
            is HomeCarousel -> typeCarousel
            else -> throw IllegalArgumentException("Invalid type of data {$position}")
        }
    }
    inner class CarouselViewHolder(itemView: View) : BaseViewHolder<HomeCarousel>(itemView) {
        override fun bind(item: HomeCarousel) {
        }
    }
}
abstract class BaseViewHolder<in T: Any>(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}

Объяснение

Возможно, этот ответ может помочь вам получить больше идей - https://stackoverflow.com/a/51110484/2674983

Хотя я нахожу обоснование в ответе неверным.Проблема не в том, что адаптер написан на Java, а в том, что он требует возврата как подтипа viewholder, так и его аргумента.В противном случае было бы легко использовать что-то вроде use-site variance .

Я думаю, что правильным способом решения этой проблемы было бы использование контравариантности:

abstract class BaseViewHolder<in T>(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}

Это потому, что объект типа T может быть чем угодно и ему нужно передать сеттеру, поэтому нам нужно только установить это в ViewHolder.Тем не менее, у контрапозитива есть интересный поворот ... подтип работает сейчас в противоположном направлении!Итак, теперь BaseViewHolder<HomeCarousel> больше не является подтипом BaseViewHolder<Any>, поскольку подтип перевернут.

Чтобы исправить это, мы могли бы выполнить «небезопасное приведение», как я делал в коде.

О, а использование BaseViewHolder<*> (проекция в виде звезды) действительно решает проблемы с подтипами,поскольку каждый экземпляр BaseViewHolder теперь является подтипом, но он накладывает ограничения как ковариации, так и контравариантности, поэтому здесь это бесполезно.

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