JavaRush /Blogue Java /Random-PT /Listas complexas facilitadas
Paul Soia
Nível 26
Kiyv

Listas complexas facilitadas

Publicado no grupo Random-PT
Olá a todos. Decidi levantar o tema das listas complexas no Android. Este tópico me intrigou no início. Pareceu-me muito difícil, mas na verdade tudo é mais simples. E acho que qualquer pessoa que agora enfrente esse problema achará o artigo útil. Todos os exemplos são escritos em Kotlin. Tentei escrever comentários em todos os lugares e deixar o mais claro possível para quem escreve em Java. Então aqui está a nossa estrutura de classes: Listas complexas facilitadas - 1MainActivity- Esta é a classe principal. ListAdapter- aqui vinculamos nossa lista para visualizar elementos. ViewHolder(existem vários deles) - esta é a nossa marcação para cada tipo de elemento. As classes de itens são dados para uma lista (pojo). Arquivos XML são marcações ( activity_mainpara a tela principal, o restante para cada tipo de elemento da lista). A primeira coisa que precisamos fazer é adicionar uma dependência para que possamos usar listas (por algum motivo essa dependência está faltando na hora de criar um projeto). No arquivo build.gradledo bloco dependenciesadicionamos a linha:
implementation "androidx.recyclerview:recyclerview:1.1.0"
Em seguida encontramos o arquivo activity_main.xml: Listas complexas facilitadas - 2Aqui existe apenas o nosso elemento para listas - RecyclerView. Adicionamos quaisquer recuos e formatação a gosto. Em seguida criamos a classe BaseViewHolder. Será abstrato, pois todas as nossas classes herdarão dele ViewHolder. É bem possível passar sem ele, mas ainda recomendo garantir que todas as classes herdadas tenham a mesma estrutura. (em Kotlin, extendse implementé usado em vez disso :. Como em Java, aqui você pode herdar de uma classe e implementar muitas interfaces) Em seguida, criamos nossas classes de dados ItemTitle: ItemGooglee ItemApple. Em Java, essas são classes comuns com construtor e getters.
data class ItemGoogle(
    val name: String,
    val product: String,
    val version: String,
    val isUse: Boolean
) : ListMarker
data class ItemApple(
    val name: String,
    val country: String,
    val year: Int
) : ListMarker
data class ItemTitle(
    val title: String,
    val amount: Int
) : ListMarker
Vamos nos concentrar na interface aqui ListMarker.
//это наш маркер. Его должны реализовать все айтемы, которые будут
//отображаться в конечном итоге в списке
interface ListMarker
Todas as nossas classes de dados implementam esta interface para convertê-los no mesmo tipo. Precisaremos disso mais tarde para nossa lista. A seguir, criamos nossos viewholders para cada elemento da lista. Não esqueça que cada um deles herda de BaseViewHolder. Nessas classes especificamos qual elemento viewcorresponde ao campo das classes de dados.
abstract class BaseViewHolder<t>(itemView: View) : RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}
class GoogleViewHolder(view: View) : BaseViewHolder<itemgoogle>(view) {

    override fun bind(item: ItemGoogle) {
        itemView.tvName.text = item.name
        itemView.tvProduct.text = item.product
        itemView.tvVersion.text = item.version
        if (item.isUse) {
            itemView.tvProduct.visibility = View.GONE
        } else {
            itemView.tvProduct.visibility = View.VISIBLE
        }
    }
}
class AppleViewHolder(view: View) : BaseViewHolder<itemapple>(view) {

    override fun bind(item: ItemApple) {
        //можем делать так
        itemView.tvName.text = item.name
        itemView.tvCountry.text = item.country
        itemView.tvYear.text = item.year.toString()

        /*----сверху и снизу два идентичных блока----*/

        //а можем сделать такой блок и не использовать в каждой строке itemView
        with(itemView) {
            tvName.text = item.name
            tvCountry.text = item.country
            tvYear.text = item.year.toString()
        }
    }
}
class TitleViewHolder(view: View) : BaseViewHolder<itemtitle>(view) {

    override fun bind(item: ItemTitle) {
        itemView.tvTitle.text = item.title
        itemView.tvAmount.text = item.amount.toString()
    }
}
Não há nenhuma lógica particular aqui, tudo é extremamente simples. Além disso, cada visualizador tem seu próprio layout: list_item_title.xmlListas complexas facilitadas - 3list_item_google.xmlListas complexas facilitadas - 4list_item_apple.xmlListas complexas facilitadas - 5agora vamos para a parte mais difícil - ListAdapter.
class ListAdapter : RecyclerView.Adapter<baseviewholder<*>>() {

    companion object {
        //задаем константы для каждого типа айтема
        private const val TYPE_TITLE = 0
        private const val TYPE_GOOGLE = 1
        private const val TYPE_APPLE = 2
    }

    //здесь можно использовать обычный ArrayList
    //сюда добавляются все айтемы, которые реализовали интерфейс ListMarker
    //How вариант можно было сделать mutableListOf<any>() и обойтись без интерфейса
    private val items = mutableListOf<listmarker>()

    internal fun swapData(list: List<listmarker>) {
        items.clear()
        items.addAll(list)
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
        return when(viewType) {
            //задаем разметку для каждого типа айтема
            TYPE_TITLE -> TitleViewHolder(parent.inflate(R.layout.list_item_title))
            TYPE_GOOGLE -> GoogleViewHolder(parent.inflate(R.layout.list_item_google))
            TYPE_APPLE -> AppleViewHolder(parent.inflate(R.layout.list_item_apple))
            else -> throw IllegalArgumentException("Invalid view type")
        }
    }

    override fun getItemViewType(position: Int): Int {
        return when (items[position]) {
            is ItemTitle -> TYPE_TITLE
            is ItemGoogle -> TYPE_GOOGLE
            is ItemApple -> TYPE_APPLE
            else -> throw IllegalArgumentException("Invalid type of item $position")
        }
    }

    override fun getItemCount(): Int {
        //этот метод определяет размер списка
        return items.size
    }

    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
        val element = items[position]
        when (holder) {
            //отправляем каждый айтем к своему ViewHolder
            is TitleViewHolder -> holder.bind(element as ItemTitle)
            is GoogleViewHolder -> holder.bind(element as ItemGoogle)
            is AppleViewHolder -> holder.bind(element as ItemApple)
            else -> throw IllegalArgumentException()
        }
    }
}
</listmarker></listmarker></any></baseviewholder<*>
Quando herdamos nossa classe de , RecyclerView.Adapterprecisaremos substituir três métodos: onCreateViewHolder,,, getItemCount. - aqui inicializamos nossas classes . — este método é responsável pelo tamanho da lista. - aqui passamos os elementos da lista para nossas classes . Para uma lista regular com um tipo de elemento, isso seria suficiente. Mas para tipos diferentes ainda precisamos redefinir o método (para isso usamos as constantes que estão no topo da nossa classe adaptadora. Em Java você pode usar variáveis ​​para isso). Também em Java, em vez de uma expressão, você pode usar um arquivo . E finalmente, vamos para nossa aula principal - . onBindViewHolderonCreateViewHolderViewHoldergetItemCountonBindViewHolderViewHoldergetItemViewTypefinalonBindViewHolderwhenifMainActivity
class MainActivity : AppCompatActivity() {

    private val adapter = ListAdapter()

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

    private fun initRecyclerView() {
        rvList.layoutManager = LinearLayoutManager(this)

        //здесь мы задаем разделитель между айтемами, чтоб они не сливались друг с другом
        val divider = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
        rvList.addItemDecoration(divider)

        rvList.adapter = adapter
        stubData()
    }

    private fun stubData() {
        val list = mutableListOf<listmarker>()
        list.add(ItemTitle("title1", 4))
        list.add(ItemGoogle("android", "product1", "17.0v", true))
        list.add(ItemGoogle("no name", "product2", "3.1v", false))
        list.add(ItemApple("macOs", "USA", 2005))
        list.add(ItemApple("iOs", "China", 2007))
        list.add(ItemTitle("title2", 2))
        list.add(ItemGoogle("map", "product3", "23.0v", true))
        list.add(ItemApple("car", "England", 2018))
        list.add(ItemTitle("title3", 0))
        //отправляем все данные в адаптер
        adapter.swapData(list)
    }
}
</listmarker>
RecyclerViewÉ aqui que nosso e é inicializado ListAdapter. Este é um código bastante padronizado que muitos encontraram. No método, stubDatapreenchi a lista com dados (como vocês podem ver, dados com elementos diferentes) e passei essa lista para o adaptador. A seguir, lançamos nosso aplicativo e devemos ver algo assim em nossa tela: Listas complexas facilitadas - 6Como você pode ver, existem diferentes elementos em uma lista, que era nosso objetivo. PS Esqueci de mencionar o Extension. Isto é o que parece:
//это расширение для класса ViewGroup. Теперь мы можем по всему проекту использовать
//короткое inflate instead of длинного LayoutInflater.from(context).inflate
fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View =
    LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
Esta não é uma classe, mas sim um arquivo, por isso não há nome dentro dele. Mas agora podemos usá-lo no adaptador de forma simples, inflateem vez de uma estrutura longa. Infelizmente, isso não é fornecido para Java, então escreva LayoutInflater.from(context).inflate. Isso é tudo. Até a próxima :) Link para GitHub
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION