RecyclerView no Android com exemplo

O que é RecyclerView?

O RecyclerView é um widget que é a versão mais flexível e avançada do GridView e ListView. É um contêiner para exibir grandes conjuntos de dados que podem ser rolados com eficiência, mantendo um número limitado de visualizações. Você pode usar o widget RecyclerView quando houver coletas de dados cujos elementos mudem no tempo de execução dependendo do evento da rede ou da ação do usuário.

Neste tutorial, você aprenderá:

Visualizações

A plataforma Android usa as classes View e ViewGroup para desenhar itens na tela. Essas classes são abstratas e estendidas a diferentes implementações para atender a um caso de uso. TextView, por exemplo, tem um propósito simples de exibir conteúdo de texto na tela. EditText estende-se da mesma classe View e adiciona mais funcionalidade para permitir que o usuário insira dados.

É possível criar nossas próprias visualizações personalizadas para conseguir mais flexibilidade ao desenvolver interfaces de usuário. A classe View fornece métodos que podemos substituir para desenhar na tela e um meio de passar parâmetros como largura, altura e nossos próprios atributos personalizados que gostaríamos de adicionar à nossa View para fazê-la se comportar como desejaríamos.

ViewGroups

A classe ViewGroup é um tipo de View, mas, ao contrário da classe View simples, cuja responsabilidade é simplesmente exibir, o ViewGroup nos dá a capacidade de colocar várias views em uma view, que podemos referenciar como um todo. Nesse caso, a visualização que é criada no nível superior à qual adicionamos outras visualizações simples (também podemos adicionar viewGroups) é chamada de 'pai' e as visualizações que são adicionadas dentro são 'filhas'.

Podemos imaginar uma View como um Array e um ViewGroup como um Array de Arrays. Dado que um Array de Arrays é um Array em si, podemos ver como um ViewGroup pode ser tratado como uma View. android:orientation=VERTICAL|HORIZONTAL

O ViewGroup também nos permite definir como os filhos são organizados dentro da visualização, por exemplo, são dispostos verticalmente ou horizontalmente. Podemos ter regras diferentes para interação dentro da View. Por exemplo, TextViews seguindo um ao outro devem ter uma distância de 12 dp, enquanto ImageViews seguidos por TextView devem ter uma distância de 5 dp.

Esse seria o caso se estivéssemos desenvolvendo nosso próprio ViewGroup do zero. Para tornar essas configurações mais fáceis, o Android fornece uma classe chamada LayoutParams, que podemos usar para inserir essas configurações.

Documentação Android fornece alguns parâmetros padrão que implementaríamos ao configurar nosso próprio ViewGroup. Alguns parâmetros comuns são aqueles que dizem respeito à largura, altura e margem. Por padrão, essas configurações têm a estrutura android: layout_height para altura, por exemplo, android: layout_width Nesse sentido, ao criar seu ViewGroup, você pode criar LayoutParams específicos para a forma como deseja que seu ViewGroup se comporte.

O Android vem com visualizações e grupos de visualizações padrão que podemos usar para fazer muitas das tarefas comuns de que precisamos. Um exemplo que mencionamos é um TextView. Essa é uma visualização simples que vem com aspectos configuráveis ​​como altura, largura, textSize e outros. Temos um ImageView para exibição de imagens e EditText como mencionamos, entre muitos outros. O Android também possui ViewGroups personalizados aos quais podemos adicionar nossas visualizações e obter o comportamento esperado.

LinearLayout

LinearLayout nos permite adicionar itens de visualização nele. LinearLayout é um atributo de orientação que determina como será apresentado na tela. Ele também tem LinearLayout.LayoutParams que ditam as regras para as visualizações dentro, por exemplo, o atributo android: center_horizontal centralizaria as visualizações ao longo do eixo horizontal, enquanto `android: center_vertical centralizaria o conteúdo da visualização ao longo do eixo vertical.

Aqui estão algumas imagens para entender a centralização. Nós consideraríamos isso como um TextView simples dentro de um espaço de 200px por 200px, os atributos de centralização fariam com que ele se comportasse da seguinte maneira.

android: center_horizontal

Conteúdo centrado horizontalmente

android: center_vertical

conteúdo centrado verticalmente

android: center

Conteúdo centrado

Componentes principais do RecyclerView

Componentes principais do RecyclerView

A seguir estão os componentes importantes do RecyclerView:

RecyclerView.Adapter

Fundamento # 1 - Padrão do Adaptador

Um adaptador é um dispositivo que transforma os atributos do sistema ou dispositivo naqueles de um dispositivo ou sistema incompatível. Alguns deles modificam os atributos de sinal ou potência, enquanto outros simplesmente adaptam a forma física de um conector a outro.

Um exemplo simples que encontramos na vida real para explicar um adaptador é quando precisamos conectar dispositivos, mas eles têm portas de conexão que não correspondem entre si. Esse pode ser o caso quando você visita um país diferente, onde usam diferentes tipos de tomadas. Se você carrega seu telefone ou carregador de laptop, seria impossível conectá-lo às tomadas elétricas. No entanto, você não desistiria, simplesmente pegaria um adaptador que ficaria entre a tomada e o carregador e permitiria que o carregamento ocorresse.

Este é o caso da programação, quando queremos conectar duas estruturas de dados para cumprir a tarefa, mas suas portas padrão não têm uma maneira de se comunicarem.

Usaremos o exemplo simples de um dispositivo e um carregador. Teremos duas instâncias de carregadores. Um americano e um britânico addView(view:View)

Em seguida, criaremos dois dispositivos AmericanToBritishChargerAdapter()

Como exemplo, podemos criar algumas instâncias dos dispositivos para tocar junto. RecyclerView.LayoutManager

Em seguida, apresentaremos o conceito de carregamento para ambos os dispositivos, adicionando um método nos dispositivos chamado charge ().

O método recebe como entrada seu respectivo carregador e faz o carregamento com base nele. RecyclerView.Adapter

Nesse caso, com base em nossa analogia, estaríamos por um motivo ou outro precisando usar um BritishCharger ao usar um AmericanDevice ou vice-versa.

No mundo da programação, isso geralmente ocorre quando se mistura bibliotecas que oferecem a mesma funcionalidade (em nosso contexto, nossa funcionalidade compartilhada está cobrando). Precisamos encontrar uma maneira de habilitar isso.

Se seguirmos a analogia, precisaremos ir a uma loja de eletrônicos e comprar um adaptador que nos permitiria carregar AmericanDevices fornecidos BritishChargers. Do ponto de vista da programação, seremos os fabricantes do adaptador.

Faremos um adaptador para um que corresponda ao padrão exato que precisaríamos para criar o outro. Vamos implementá-lo como uma classe da seguinte maneira. Não precisa necessariamente ser uma classe e pode ser uma função que destaca o que o padrão do adaptador geralmente faz. Usaremos uma classe, pois corresponde à maioria dos usos no Android.

 var arr1 = [1,2,3] //imagine a simple View as an Array //we can imagine this as a NumberTextView which doesn't really exist //but we could imagine there's one that makes it easy to use numbers var arr2 = ['a','b','c'] // We can imagine this as another simple view var nestedArr = [arr1,arr2] //in our anology, we can now group views //together and the structure that would hold that would be what we call the ViewGroup 

No mundo da programação, a diferença nas tomadas é análoga à diferença nos métodos internos usados ​​para carregar. Os carregadores com métodos diferentes impossibilitariam o uso dos carregadores.

 class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var charginPower = 5 } 

Tentar chamar o método charge () em myBritishDevice com americanChargerIFound não funcionaria porque AmericanDevice só aceita AmericanCharger

Portanto, é impossível fazer isso

 class AmericanDevice() class BritishDevice() 

Neste cenário, o adaptador que criamos

AmericanToBritishChargerAdapter agora pode ser útil. Podemos usar o método returnNewCharger () para criar um novo BritishCharger, que podemos usar para cobrar. Tudo o que precisamos é criar uma instância de nosso adaptador e alimentá-lo com o AmericanCharger que temos, e ele criará um BritishCharger que podemos usar

 var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice() 

RecyclerView.LayoutManager

Ao lidar com um ViewGroup, teríamos Views colocadas dentro dele. É LayoutManager que teria a tarefa de descrever como as visualizações são dispostas internamente.

Para fins de comparação, ao trabalhar com Linearlayout ViewGroup, o caso de uso que queremos é a capacidade de colocar os itens vertical ou horizontalmente. Isso é facilmente implementado adicionando um atributo de orientação, que nos diz como o layout linear será colocado na tela. Podemos fazer isso usando

 sealed trait Device class AmericanDevice : Device{ fun charge(charger:AmericanCharger){ //Do some American charging } } class BritishDevice: Device{ fun charge(charger:BritishCharger){ //Do some British charging } } 
atributo.

Também temos outro ViewGroup chamado GridLayout, seu caso de uso é quando queremos colocar Views em uma estrutura de grade retangular. Isso pode ocorrer por motivos como tornar os dados que estamos apresentando ao usuário do aplicativo fáceis de consumir. Por design, o GridLayout permite configurações para ajudá-lo a alcançar este objetivo, tendo configurações onde podemos definir as dimensões da grade, por exemplo, podemos ter uma grade 4x4, grade 3 x 2.

RecyclerView.ViewHolder

O ViewHolder é uma classe abstrata que também estendemos do RecyclerView. O ViewHolder nos fornece métodos comuns para nos ajudar a fazer referência a uma View que colocamos no RecyclerView, mesmo depois que o maquinário de Reciclagem no RecyclerView mudou várias referências que não conhecemos.

Listas Grandes

RecyclerViews são usados ​​quando queremos apresentar um conjunto realmente grande de Views ao usuário, ao mesmo tempo não esgotando nossa RAM em nosso dispositivo para cada instância da View criada.

Se fôssemos pegar o caso de uma Lista de Contatos, teríamos uma ideia geral de como um contato ficaria na lista. O que faríamos então é criar um layout de modelo - que na verdade é uma Visualização - com slots onde vários dados de nossa lista de contatos serão preenchidos. A seguir está um pseudocódigo que explica todo o propósito:

 class AmericanToBritishChargerAdapter(theAmericanCharger:AmericanCharger){ fun returnNewCharger(): BritishCharger{ //convert the American charger to a BritishCharger //we would change the American charging functionality //to British charging functionality to make sure the //adapter doesn't destroy the device. The adapter could //, for example, control the power output by dividing by 2 //our adapter could encompass this functionality in here var charingPower:Int = charger.chargingPower / 2 var newBritishCharger = new BritishCharger() newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2 return newBritishCharger } } 

Teríamos então uma ContactList desta natureza

 var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() 

Se for o caso, estávamos codificando o conteúdo, não teríamos uma forma programática de adicionar novo conteúdo à lista sem reescrever o aplicativo. Felizmente para nós. Adicionar uma visualização a um ViewGroup é suportado por um

 var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound) 
método.

Mesmo se for esse o caso, não é assim que o RecyclerView obtém visualizações filhas adicionadas a ele.

Em nosso caso de uso, teríamos uma longa lista de contatos. Para cada contato na lista, precisaríamos criar OneContactView e preencher os dados dentro de View para corresponder aos campos em nossa classe Contact. Então, quando tivermos a visualização, precisaremos adicioná-la ao RecyclerView para mostrar a lista.

var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() //We create the adapter and feed it the americanCharger var myAdapter = AmericanToBritishChargerAdapter(theAmericanCharger) //calling returnNewCharger from myAdapter would return a BritishCharger var britishChargerFromAdapter = myAdapter.returnNewCharger() //and once we have the britishCharger we can now use it myBritishDevice.charge(britishChargerFromAdapter) 

Temos uma matriz de contatos chamada OneContactView. Ele contém slots para obter conteúdos da classe Contato e exibi-los. No RecyclerView, temos que adicionar Views a ele para que possa nos ajudar com sua capacidade de reciclagem.

O RecyclerView realmente não nos permite adicionar visão, mas nos permite adicionar um ViewHolder. Portanto, neste cenário, temos duas peças que queremos conectar, mas não combinam. É aqui que entra nosso adaptador. O RecyclerView nos fornece um adaptador muito parecido com o nosso

//OneContactView {{PlaceHolderForName}} {{PlaceHolderForAddress}} {{PlaceHolderForProfilePicture}} {{PlaceHolderForPhoneNumber}} 
de antes, isso nos permitiu converter nosso AmericanCharger, que era inutilizável com nosso BritishDevice, em algo utilizável, semelhante a um adaptador de energia na vida real.

Nesse cenário, o adaptador pegaria nosso array de Contatos e nossa View, e a partir daí geraria ViewHolders que o RecyclerView está disposto a aceitar.

O RecyclerView fornece uma interface que podemos estender para criar nosso adaptador por meio da classe RecyclerView.Adapter. Dentro desse adaptador está uma forma de criar a classe ViewHolder com a qual o RecyclerView deseja trabalhar. Então, o que temos é a mesma situação de antes, mas com uma coisa extra, que é o Adaptador.

Temos uma matriz de contatos, uma visão para exibir um contato OneContactView. Um RecyclerView é uma lista de Views que fornecem serviços de reciclagem, mas só aceita ViewHolders

Mas, neste cenário, agora temos a classe RecyclerView.Adapter, que possui um método para criar ViewHolders internamente.

 

O RecyclerView.ViewHolder é uma classe abstrata que pega nosso View como um argumento e o converte em um ViewHolder.

Ele usa o padrão de invólucro usado para estender as habilidades das classes.

Fundamento # 2 - Padrão de invólucro

Usaremos um exemplo simples para demonstrar como podemos fazer os animais falarem.

 data class Contact(var name:String, var address:String, var pic:String, var phoneNumber:Int) var contact1 = Contact('Guru','Guru97', 'SomePic1.jpg', 991) var contact2 = Contact('Guru','Guru98', 'SomePic2.jpg', 992) var contact3 = Contact('Guru','Guru99', 'SomePic3.jpg', 993) var myContacts:ArrayList = arrayListOf(contact1,contact2,contact3) 

No exemplo acima, temos dois animais. Se por acaso quiséssemos adicionar um método para fazer a fala, mas o autor da biblioteca não foi divertido, ainda poderíamos encontrar um jeito. O que precisamos é um invólucro para nossa classe Animal. Faríamos isso considerando o Animal como um construtor para nossa classe

fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder 

Agora podemos passar uma instância animal para o SpeechPoweredAnimalByWrapper. Chamar o método sound () nele chamaria o método animal sound () passado. Também temos um método speak () adicional, que conta como uma nova funcionalidade que adicionamos aos animais passados. Podemos usá-lo da seguinte maneira:

 sealed trait Animal{ fun sound():String } data class Cat(name:String):Animal{ fun sound(){ 'Meow' } } data class Dog(name:String):Animal{ fun sound(){ 'Woof' } } var cat1 = Cat('Tubby') var dog1 = Dog('Scooby') cat1.sound() //meow dog1.sound() //woof 

Usando esse padrão, podemos fazer aulas e adicionar funcionalidades. Tudo que precisaríamos é passar uma instância de classe e novos métodos definidos por nossa classe de empacotamento.

Em nosso caso acima, usamos uma classe concreta. Também é possível implementar o mesmo em uma classe Abstract. Precisamos fazer é adicionar alterar a classe SpeechPoweredAnimalByWrapper para abstrata e pronto. Vamos mudar o nome da classe para algo mais curto para torná-lo mais legível.

 class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println('Hello, my name is ${myAnimal.name}') } } 

É o mesmo de antes, mas significaria outra coisa. Em uma classe normal, podemos ter uma instância de uma classe da mesma forma que criamos cat1 e dog1. As classes abstratas, no entanto, não devem ser instanciadas, mas sim estender outras classes. Então, como usaríamos a nova classe abstrata SpeechPowered (var myAnimal: Animal). Podemos usá-lo criando novas classes que irão estendê-lo e, por sua vez, obter sua funcionalidade.

Em nosso exemplo, criaremos uma classe SpeechPoweredAnimal que estende a classe

var cat1 = Cat('Garfield') cat1.sound()//'meow' cat1.speak()// doesn't work as it isn't implemented var talkingCat = new SpeechPoweredAnimalByWrapper(cat1) talkingCat.sound() //'meow' the sound method calls the one defined for cat1 talkingCat.speak() //'Hello, my name is Garfield' 
 abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println('Hello, my name is ${myAnimal.name}') } } 

Este é o mesmo padrão usado no ViewHolder. A classe RecyclerView.ViewHolder é uma classe abstrata que adiciona funcionalidade à View, da mesma forma que adicionamos o método speak aos animais. A funcionalidade adicionada é o que o faz funcionar ao lidar com o RecyclerView.

É assim que criaríamos um OneContactViewHolder a partir de OneContactView

 class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal) 

RecyclerView tem um adaptador que nos permite conectar nosso array de contatos ao ContactsView com o RecyclerView

Adicionando uma Visualização

O ViewGroup não redesenha o ViewGroup automaticamente, mas segue uma programação específica. Pode ser que no seu dispositivo ele seja redesenhado a cada 10 ms ou 100 ms, ou se escolhermos um número absurdo, digamos 1 minuto quando adicionamos um View a um ViewGroup, você verá as alterações 1 minuto depois, quando o ViewGroup 'for atualizado'.

RecyclerView.Recycle

Fundamento # 3. Cache

Um dos melhores exemplos de onde fazemos atualizações regularmente é no navegador. Vamos imaginar, por exemplo, que o site que estamos visitando é estático e não está enviando conteúdo dinamicamente, precisaríamos continuar atualizando para ver as mudanças.

Para este exemplo, vamos imaginar que o site em questão seja o Twitter. Teríamos uma série de tweets estáticos listados e a única maneira de ver os novos Tweets seria clicando no botão Atualizar para recuperar o conteúdo.

Repintar a tela inteira é obviamente uma coisa cara. Se fôssemos imaginar se fosse esse o caso, tínhamos largura de banda limitada com nossa operadora de telefonia. E nossa lista de tweets tinha muitas imagens e vídeos, seria caro baixar novamente todo o conteúdo da página a cada atualização.

Precisaríamos de uma maneira de armazenar os Tweets já carregados e garantir que nossa próxima solicitação tenha a capacidade de dizer os Tweets que já possui. Portanto, ele não baixa tudo de novo e apenas obtém os novos Tweets que possui e também verifica se algum Tweet que foi salvo localmente não está mais lá para que possa excluí-lo localmente. O que estamos descrevendo é chamado de Cache.

As informações que enviamos ao site sobre os conteúdos que possuímos são chamadas de meta-dados. Portanto, na verdade, não estamos apenas dizendo 'queremos carregar seu site', dizemos 'queremos carregar seu site, e aqui estão alguns dos conteúdos que já salvamos da última vez que carregamos, por favor, use-os para envie-nos apenas o que não está lá, por isso não usamos muita largura de banda porque não temos muitos recursos. '

Chamadas de layout - a lista de tweets deve ser maluca

Um exemplo de chamadas de layout é scrollToPosition

Este é um exemplo comum que está presente em coisas como aplicativos de bate-papo. Se alguém em um tópico de bate-papo responde a um balão de bate-papo anterior, alguns aplicativos de bate-papo incluem a resposta e um link para o balão de bate-papo, que ao clicar o leva para onde sua mensagem original estava.

No caso, chamamos este método antes de termos um LayoutManager adicionado ao nosso RecyclerView e antes de termos um RecyclerView.Adapter, o scrollToPosition (n: Int) é simplesmente ignorado.

Comunicação entre os componentes do RecyclerView

Fundamento # 4. Callbacks

O RecyclerView, ao fazer seu trabalho, tem muitas partes móveis. Tem que lidar com o LayoutManager que nos diz como organizar as visualizações, linearmente ou em grade. Ele tem que lidar com um Adaptador que faz o trabalho de converter nossos itens contactList em Views OneContactView e então em ViewHolders OneContactViewHolder que o RecyclerView está disposto a trabalhar dentro dos métodos que ele fornece para nós.

A matéria-prima para o RecyclerView são nossas visualizações, por exemplo OneContactView e fonte de dados.

 var cat1 = Cat('Tubby') var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //'Hello, my name is Tubby' 

Usamos um cenário simples como ponto de partida para ter uma ideia do que o RecyclerView está tentando alcançar.

Um caso básico de quando teríamos uma matriz estática de 1000 contatos que queremos mostrar ao usuário é fácil de entender.

O maquinário RecyclerView realmente começa a ganhar vida quando a lista não está mais estática.

Com uma lista dinâmica, temos que pensar no que acontece com a Visualização na tela quando adicionamos um item à lista ou removemos um item da lista.

RecyclerView.LayoutManager

Além de decidir como nossas visualizações devem ser dispostas linearmente ou em uma grade. O LayoutManager faz um grande trabalho por baixo do capô para ajudar o Reciclador a saber quando fazer a Reciclagem.

É responsável por acompanhar as Visualizações atualmente visíveis na Tela e comunicar essas informações ao mecanismo de Reciclagem. Conforme o usuário rola para baixo, o gerenciador de Layout é responsável por informar ao sistema de Reciclagem as Visualizações que saem de foco no topo para que possam ser reutilizadas ao invés de permanecerem ali consumindo memória ou ao invés de destruí-las e ter que criar novos.

O que isso significa é que o LayoutManager precisa manter o controle de onde o usuário está enquanto rola nossa lista. Ele faz isso por ter uma lista de posições que são a base do índice, ou seja, o primeiro item deve começar de 0 e aumentar para corresponder ao número de itens em nossa lista.

Se pudermos visualizar 10 itens em nossa lista de, digamos, 100, no início, o LayoutManager está ciente de que tem em foco view-0 até a View-9 Conforme rolamos, LayoutManager é capaz de calcular as visualizações que saem de foco.

O LayoutManager é capaz de liberar essas visualizações para o mecanismo de Reciclagem para que possam ser reutilizadas (novos dados podem ser vinculados a elas, por exemplo, os dados de contato de uma Visualização podem ser removidos e novos dados de contato do próximo segmento podem substituir os espaços reservados).

Este seria um caso feliz se a lista que temos fosse estática, mas um dos casos de uso mais comuns de usar um RecyclerView é com listas dinâmicas onde os dados podem vir de um endpoint online ou mesmo talvez de um sensor. Não apenas os dados são adicionados, mas os dados em nossa Lista às vezes são removidos ou atualizados.

O estado dinâmico de nossos dados pode tornar muito difícil raciocinar sobre o LayoutManager. Por esse motivo, o LayoutManager mantém uma lista própria sobre os itens e posições, que é separada da Lista que o componente Reciclagem usa. Isso garante que ele faça seu trabalho de layout corretamente.

Ao mesmo tempo, o LayoutManager do RecyclerView não quer deturpar os dados que possui. Para operar corretamente, o LayoutManager sincroniza com o RecyclerView.Adapter em determinados intervalos (60ms) compartilhando informações sobre nossos itens de lista. ou seja, itens adicionados, atualizados, removidos, movidos de uma posição para outra). O LayoutManager, ao receber esta informação, reorganiza o conteúdo da Tela para corresponder às alterações quando necessário.

Muitas das operações principais que lidam com RecylerView giram em torno da comunicação entre RecyclerView.LayoutManager e RecyclerView.Adapter que armazena nossas listas de dados às vezes estáticos ou às vezes dinâmicos.

Além disso, RecyclerView nos apresenta métodos que podemos usar para ouvir em eventos como onBindViewHolder quando nosso RecyclerView.Adapter vincula conteúdo de nossa Lista, por exemplo, um contato para um ViewHolder para que ele agora se acostume a exibir as informações na tela.

Outro é onCreateViewHolder, que nos informa quando o RecyclerView. O adaptador pega uma View normal como OneContactView e a converte em um item ViewHolder com o qual o RecyclerView pode trabalhar. De nosso ViewHolder para uso pelo RecyclerView. onViewDetached

Além dos principais mecanismos que permitem a Reciclagem. O RecyclerView fornece maneiras de personalizar o comportamento sem afetar a reciclagem.

A reutilização de visualizações torna difícil fazer coisas comuns que estamos acostumados a fazer com visualizações estáticas, como reagir a eventos onClick.

Como estamos cientes de que

 //The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView) 
que apresenta as visualizações ao usuário pode, por um momento, ter uma lista diferente de itens de
contactList:Array
que contém a lista que armazenamos em um banco de dados ou transmitimos de uma fonte. Colocar eventos OnClick diretamente nas visualizações pode levar a um comportamento inesperado, como excluir o contato errado ou alterá-lo.

Gradle

Se quisermos usar o RecyclerView, precisamos adicioná-lo como uma dependência em nosso arquivo build .gradle.

No exemplo a seguir, usamos a implementação 'androidx.recyclerview: recyclerview: 1.1.0', que é a versão mais atual de acordo com este artigo.

Depois de adicionar a dependência ao nosso arquivo Gradle, o Android Studio solicitará que sincronizemos as alterações,

É assim que nosso arquivo Gradle ficará após adicionar um RecyclerView em um projeto vazio apenas com os padrões.

 apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 29 buildToolsVersion '29.0.2' defaultConfig { applicationId 'com.on2vhf.learnrecycler' minSdkVersion 17 targetSdkVersion 29 versionCode 1 versionName '1.0' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation'org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.recyclerview:recyclerview:1.1.0' } 

Temos apenas um arquivo de layout no momento. Começaremos com um exemplo simples onde usaremos um RecyclerView para mostrar uma lista de nomes de frutas na tela.

Lista de itens

Iremos navegar até o nosso arquivo MainActivity e criaremos um array com os nomes das frutas logo antes do método onCreate () que foi gerado durante a configuração.

 package com.on2vhf.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { var fruitNames:Array = arrayOf('Banana', 'Mango', 'Passion fruit', 'Orange', 'Grape') override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } 

Nosso próximo objetivo será apresentar esta lista na tela usando um RecyclerView.

Para isso, navegaremos até o diretório de layout que contém nossos Layouts e criaremos uma Visualização que será responsável por mostrar uma fruta.

Layout a ser usado para cada item em nossa lista

 

No TextView acima, adicionamos um campo de id que será usado para identificar uma View.

Não é gerado por padrão. Temos em nosso TextView o id fruitName para corresponder aos dados, que serão vinculados a ele.

Adicionando o RecyclerView ao layout principal

Na mesma atividade, existe o arquivo de layout main_layout.xml que foi gerado para nós por padrão.

Se escolhermos um projeto vazio. Ele terá gerado um XML contendo um ConstraintLayout e dentro dele estará um TextView com o texto 'Hello'.

Excluiremos todo o conteúdo e faremos com que o layout contenha apenas o RecyclerView conforme abaixo:

 

Também adicionamos um atributo id para o RecyclerView que usaremos para referenciá-lo em nosso código.

 android:id='@+id/fruitRecyclerView' 

Em seguida, navegaremos de volta ao nosso arquivo MainActivity. Usando os id's que criamos, poderemos fazer referência aos View's que acabamos de criar.

Começaremos referenciando o RecyclerView usando o método findViewById () fornecido pelo Android. Faremos isso em nosso método onCreate ().

Nosso método onCreate () terá a seguinte aparência.

 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } 

Crie um ViewHolder

A seguir, criaremos um RecyclerView.ViewHolder que é responsável por pegar nossa View e convertê-la em ViewHolder, que o RecyclerView usa para exibir nossos itens.

Faremos isso logo após nosso divertido método onCreate ().

 package com.on2vhf.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array = arrayOf('Banana', 'Mango', 'Passion fruit', 'Orange', 'Grape') override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) } 

Crie um RecyclerViewAdapter

A seguir, criaremos uma classe FruitArrayAdapter que estende a classe RecyclerView.Adapter.

O FruitArrayAdapter que criamos será responsável por fazer o seguinte.

Receberá nomes de frutas da matriz de frutas. Ele criará um ViewHolder usando nossa visão one_fruit_view.xml. Em seguida, vinculará a fruta a um ViewHolder e vinculará dinamicamente o conteúdo à visão que criamos one_fruit_view.xml.

 package com.on2vhf.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array = arrayOf('Banana', 'Mango', 'Passion fruit', 'Orange', 'Grape') override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) class FruitArrayAdapter(var fruitArray: Array) : RecyclerView.Adapter() } 

O Android Studio adicionará rabiscos vermelhos em nosso FruitArrayAdapter, informando que precisamos implementar um método que o RecyclerView pode usar para conectar nosso array a um ViewHolder que o RecyclerView pode usar.

 class FruitListAdapter(var fruitArray: Array) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { } override fun getItemCount(): Int { } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } } 

Começaremos com a parte mais fácil do código gerado é o método getItemCount (). Sabemos como obter o número de itens em nosso array chamando o método array.length.

class FruitListAdapter(var fruitArray: Array) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } } 

Em seguida, implementaremos o método override fun onCreateViewHolder.

É aqui que o RecyclerView nos pede para ajudá-lo a construir um FruitHolder para ele.

Se nos lembrarmos, é assim que nossa classe FruitViewHolder se parecia:

class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

Requer nosso fruitView que criamos como um arquivo xml one_fruit_view.xml

Podemos ser capazes de criar uma referência para este xml e convertê-lo em uma visão da seguinte maneira.

 class FruitListAdapter(var fruitArray: Array) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } } 

O bit restante é o override

fun onBindViewHolder(holder: FruitViewHolder, position: Int)

O RecyclerView.Adapter pergunta com um inteiro de posição, que usaremos para buscar um item de nossa lista. Ele também nos fornece um suporte para que possamos vincular o item obtido do fruitArray à View que está sendo mantida dentro do view holder.

A View mantida dentro de um ViewHoder pode ser acessada através do campo ViewHolder.itemView. Assim que obtivermos a visualização, podemos usar o id fruitName que criamos anteriormente para definir o conteúdo.

 override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } 

Com isso, nosso FruitArrayAdapter está completo e tem a seguinte aparência.

 class FruitListAdapter(var fruitArray: Array) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } } 

Finalmente, estamos prontos para conectar as peças restantes de nosso RecyclerView. Que estão criando um LayoutManager, que dirá ao RecyclerView como exibir o conteúdo da lista. Mostrar de maneira linear usando LinearLayoutManager ou em uma grade usando GridLayoutManager ou StaggeredGridLayoutManager.

Criar Gerenciador de Layout

Vamos voltar para dentro de nossa função onCreate e adicionar o LayoutManager

 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout } 

Conecte nosso adaptador aos itens e configure-o no RecyclerView

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter } 

Também criamos uma instância fruitListAdapter e a alimentamos com o array de nomes de frutas.

E basicamente, terminamos.

O arquivo MainActivity.kt completo tem a seguinte aparência.

 package com.on2vhf.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array = arrayOf('Banana', 'Mango', 'Passion fruit', 'Orange', 'Grape') override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) class FruitListAdapter(var fruitArray: Array) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } } } 

Baixe o Projeto