Android RecyclerView: O que é, aprenda com exemplos simples
⚡ Resumo Inteligente
RecyclerView é um componente avançado. Android Widget que exibe grandes conjuntos de dados roláveis de forma eficiente, reciclando um número limitado de visualizações. Ele utiliza um adaptador, um gerenciador de layout e um contêiner de visualização para vincular coleções de dados que mudam em tempo de execução.

O que é o RecyclerView Android?
O Visualização do Reciclador O RecyclerView é uma versão mais flexível e avançada do GridView e do ListView. É um contêiner para exibir grandes conjuntos de dados que podem ser rolados de forma eficiente, mantendo um número limitado de visualizações. Você pode usar o widget RecyclerView quando tiver coleções de dados cujos elementos mudam em tempo de execução, dependendo de um evento de rede ou da ação do usuário.
Visualizações
O Android A plataforma usa as classes View e ViewGroup para desenhar itens na tela. Essas classes são absolutas.tracAs classes `TextView` e `EditText` são estendidas para diferentes implementações, adequando-se a casos de uso específicos. `TextView`, por exemplo, tem o propósito simples de exibir conteúdo de texto na tela. `EditText` estende a mesma classe `View` e adiciona mais funcionalidades para permitir que o usuário insira dados.
É possível criar nossas próprias visualizações personalizadas para obter mais flexibilidade durante o desenvolvimento.ping interfaces de usuário. A classe View fornece métodos que podemos sobrescrever 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 que ela se comporte da maneira desejada.
Ver grupos
A classe ViewGroup é um tipo de View, mas, ao contrário da classe View simples, cuja responsabilidade é apenas exibir, o ViewGroup nos dá a capacidade de colocar várias views em uma única view, que podemos referenciar como um todo. Nesse caso, a View criada no nível superior, à qual adicionamos outras views simples, é chamada de "pai", e as views adicionadas dentro dela são chamadas de "filhos".
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.
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 analogy, we can now group views //together and the structure that would hold that would be what we call the ViewGroup
O ViewGroup também nos permite definir como os elementos filhos são organizados dentro da view, por exemplo, se são dispostos verticalmente ou horizontalmente. Podemos ter regras diferentes para interação dentro da View. Por exemplo, TextViews subsequentes devem ter uma distância de 12dp, enquanto um ImageView seguido por um TextView deve ter uma distância de 5dp.
Isso aconteceria se fôssemos desenvolvedores.ping Criamos nosso próprio ViewGroup do zero. Para facilitar essas configurações, Android fornece uma classe chamada LayoutParams, que podemos usar para inserir essas configurações.
O Android documentação Fornece alguns parâmetros padrão que implementamos ao configurar nosso próprio ViewGroup. Alguns parâmetros comuns dizem respeito à largura, altura e margem. Por padrão, essas configurações têm a estrutura android:layout_height para altura e android:layout_width para largura. Nesse sentido, ao criar seu ViewGroup, você pode criar LayoutParams específicos para definir o comportamento desejado do seu ViewGroup.
Android O pacote vem com Views e ViewGroups padrão que podemos usar para realizar muitas das tarefas comuns de que precisamos. Um exemplo que já mencionamos é o TextView. Trata-se de uma view simples com aspectos configuráveis, como altura, largura e tamanho do texto. Também temos o ImageView para exibir imagens e o EditText, como já mencionamos, entre muitos outros. Android também possui ViewGroups personalizados aos quais podemos adicionar nossas Views e obter o comportamento esperado.
Layout Linear
O LinearLayout permite adicionar itens View a ele. O LinearLayout possui um atributo de orientação que define como ele será disposto na tela. Ele também possui o LinearLayout.LayoutParams, que define as regras para as views internas. Por exemplo, o atributo android:center_horizontal centraliza as views no eixo horizontal, enquanto android:center_vertical centraliza o conteúdo da view no eixo vertical.
Aqui estão algumas imagens para ajudar a entender o conceito de centralização. Vamos considerar 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:centro_horizontal
Conteúdo centralizado horizontalmente
android:centro_vertical
Conteúdo centralizado verticalmente
andróide:centro
Conteúdo centrado
Componentes principais do RecyclerView
Componentes principais do RecyclerView
A seguir estão os componentes importantes do RecyclerView:
RecyclerView.Adapter
Base #1 – Padrão Adaptador
Um adaptador é um dispositivo que transforma os atributos de um sistema ou dispositivo nos de um sistema ou dispositivo incompatível. Alguns adaptadores modificam os atributos de sinal ou energia, enquanto outros simplesmente adaptam a forma física de um conector a outro.
Um exemplo simples da vida real que explica a utilidade de um adaptador é quando precisamos conectar dispositivos, mas as entradas deles não são compatíveis. Isso pode acontecer quando você visita um país diferente, onde as tomadas são diferentes. Se você levar o carregador do seu celular ou notebook, será impossível conectá-lo às tomadas locais. No entanto, você não desistiria e simplesmente usaria um adaptador, que se encaixa entre a tomada e o carregador, permitindo o carregamento.
Isso acontece na programação quando queremos conectar duas estruturas de dados para realizar uma tarefa, mas suas portas padrão não possuem uma maneira de se comunicarem entre si.
Usaremos o exemplo simples de um dispositivo e um carregador. Teremos dois carregadores, um americano e um britânico.
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var chargingPower = 5 }
Em seguida, criaremos dois dispositivos.
class AmericanDevice() class BritishDevice()
Como exemplo, podemos então criar algumas instâncias dos dispositivos para tocar junto.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Em seguida, introduziremos o conceito de carregamento para ambos os dispositivos, adicionando um método nos dispositivos chamado `charge()`. O método recebe o respectivo carregador e realiza o carregamento com base nele.
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 } }
Nesse caso, com base em nossa analogia, por um motivo ou outro, precisaríamos usar um carregador britânico ao usar um dispositivo americano, ou vice-versa.
No mundo da programação, isso geralmente ocorre quando se misturam bibliotecas que oferecem a mesma funcionalidade (em nosso contexto, nossa funcionalidade compartilhada é cobrada). Precisaríamos encontrar uma maneira de permitir isso.
Seguindo a analogia, precisaríamos ir a uma loja de eletrônicos e comprar um adaptador que nos permitisse carregar dispositivos americanos com carregadores britânicos. Do ponto de vista da programação, seríamos os fabricantes do adaptador.
Criaremos um adaptador que corresponda exatamente ao padrão necessário para criar o outro. Implementaremos isso como uma classe, conforme descrito a seguir. Não precisa ser necessariamente uma classe; poderia ser uma função que exemplificasse o que o padrão de adaptador geralmente faz. Usamos uma classe porque ela se alinha com a maioria dos usos em [nome da plataforma/framework]. Android.
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 chargingPower:Int = theAmericanCharger.chargingPower / 2 var newBritishCharger = new BritishCharger() newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2 return newBritishCharger } }
No mundo da programação, a diferença entre os sockets é análoga à diferença nos métodos de carregamento. Carregadores com métodos diferentes tornariam impossível o uso de alguns deles.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Tentar chamar o método charge() em myBritishDevice com americanChargerIFound não funcionaria, pois o AmericanDevice só aceita um AmericanCharger. Portanto, é impossível fazer isso:
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
Nesse cenário, o adaptador que criamos, AmericanToBritishChargerAdapter, pode ser muito útil. Podemos usar o método returnNewCharger() para criar um novo BritishCharger, que podemos usar para carregar. Tudo o que precisamos fazer é criar uma instância do nosso adaptador e fornecer a ela o AmericanCharger que temos, e ele criará um BritishCharger que podemos usar.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() //We create the adapter and feed it the americanCharger var myAdapter = AmericanToBritishChargerAdapter(americanChargerIFound) //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)
RecyclerView.LayoutManager
Ao lidar com um ViewGroup, temos Views inseridas dentro dele. O LayoutManager tem a função de descrever como as Views são dispostas internamente.
Para efeito de comparação, ao trabalhar com um ViewGroup de LinearLayout, o caso de uso desejado é a capacidade de posicionar os itens verticalmente ou horizontalmente. Isso é facilmente implementado adicionando um atributo de orientação, que nos informa como o LinearLayout será posicionado na tela. Podemos fazer isso usando o android:orientation=VERTICAL|HORIZONTAL atributo.
Temos também outro ViewGroup chamado GridLayout. Seu uso se dá quando queremos organizar as Views em uma estrutura de grade retangular. Isso pode ser útil, por exemplo, para facilitar a visualização dos dados apresentados ao usuário do aplicativo. Por padrão, o GridLayout permite configurações que ajudam a atingir esse objetivo, definindo as dimensões da grade; por exemplo, podemos ter uma grade 4x4 ou uma grade 3x2.
RecyclerView.ViewHolder
O ViewHolder é um abstracA classe `ViewHolder`, que também herdamos de `RecyclerView`, nos fornece métodos comuns que nos ajudam a referenciar uma `View` que colocamos no `RecyclerView`, mesmo após o mecanismo de reciclagem do `RecyclerView` ter alterado várias referências que desconhecemos.
Listas grandes
Os RecyclerViews são usados quando queremos apresentar um conjunto realmente grande de Views ao usuário, sem esgotar o espaço disponível. RAM em nosso dispositivo para cada instância da Visualização criada.
Se considerarmos o caso de uma lista de contatos, teremos uma ideia geral de como um contato aparecerá na lista. O que faremos então é criar um layout de modelo, que na verdade é uma View, com espaços onde vários dados da nossa lista de contatos serão preenchidos. O pseudocódigo a seguir explica todo o propósito:
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
Teríamos então uma lista de contatos com o seguinte formato:
<ContactList> </ContactList>
Se estivéssemos codificando o conteúdo diretamente no código, não teríamos uma maneira programática de adicionar novo conteúdo à lista sem reescrever o aplicativo. Felizmente, adicionar uma View a um ViewGroup é suportado por um... addView(view:View) método. Mesmo assim, não é assim que o RecyclerView recebe visualizações filhas.
Em nosso caso de uso, teríamos uma longa lista de contatos. Para cada contato na lista, precisaríamos criar uma `OneContactView` e preencher os dados dentro da View para corresponder aos campos da nossa classe `Contact`. Depois de criar a View, precisaríamos adicioná-la ao `RecyclerView` para exibir a lista.
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<Contact> = arrayListOf<Contact>(contact1,contact2,contact3)
Temos um array de contatos. O OneContactView contém slots para receber e exibir o conteúdo da classe Contact. No RecyclerView, precisamos adicionar Views para que ele possa realizar a reciclagem.
O RecyclerView não nos permite adicionar uma view diretamente, mas permite adicionar um ViewHolder. Nesse cenário, temos duas partes que queremos conectar, mas que não são compatíveis. É aí que entra o adaptador. O RecyclerView nos fornece um adaptador, semelhante ao nosso... AmericanToBritishChargerAdapter() 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 receberia nosso array de contatos e nossa View e, a partir daí, geraria ViewHolders que o RecyclerView estaria disposto a aceitar.
O RecyclerView fornece uma interface que podemos estender para criar nosso adaptador por meio da classe RecyclerView.Adapter. Dentro desse adaptador, há uma maneira de criar a classe ViewHolder com a qual o RecyclerView deseja trabalhar. Portanto, temos a mesma situação de antes, mas com um elemento adicional: o adaptador.
Temos um array de contatos, uma view para exibir um contato (OneContactView) e um RecyclerView, que é uma lista de Views que fornece serviços de reciclagem, mas só aceita ViewHolders. Nesse cenário, temos agora uma classe RecyclerView.Adapter, que possui um método para criar ViewHolders internamente.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
O RecyclerView.ViewHolder é um objeto abstrato.tracA classe `ViewHolder` recebe nossa `View` como argumento e a converte em um `ViewHolder`. Ela utiliza o padrão `wrapper`, que é usado para estender as funcionalidades de classes.
Base #2 – Padrão de wrapper
Usaremos um exemplo simples para demonstrar como podemos fazer os animais falarem.
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
No exemplo acima, temos dois animais. Se, por acaso, quiséssemos adicionar um método para fazê-los falar, mas o autor da biblioteca não fosse muito solícito, ainda assim poderíamos encontrar uma solução. Precisaríamos de um wrapper para nossa classe Animal. Faríamos isso recebendo o Animal como construtor da nossa classe.
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Agora podemos passar uma instância de animal para o SpeechPoweredAnimalByWrapper. Chamar o método sound() dessa instância chamará o método sound() do animal passado como parâmetro. Também temos um método speak() adicional, que conta como uma nova funcionalidade adicionada aos animais passados como parâmetro. Podemos usá-lo da seguinte forma:
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"
Usando esse padrão, podemos pegar classes e adicionar funcionalidades. Tudo o que precisamos é passar uma instância da classe e novos métodos definidos pelo nosso wrapper.ping classe.
No caso acima, usamos uma classe concreta. Também é possível implementar o mesmo em uma classe abstrata.tracclasse t. Precisaríamos alterar a classe SpeechPoweredAnimalByWrapper para abstract, e terminamos. Vamos mudar o nome da turma para algo mais curto para facilitar a leitura.
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
É o mesmo que antes, mas significaria algo diferente. Em uma classe normal, podemos ter uma instância de uma classe da mesma forma que criamos cat1 e dog1. AbstracAs classes `t`, no entanto, não são feitas para serem instanciadas, mas sim para estender outras classes. Então, como usaríamos o novo `SpeechPowered(var myAnimal:Animal)`?tracQual classe você usa? Podemos utilizá-la criando novas classes que a estenderão e, por sua vez, obterão sua funcionalidade.
Em nosso exemplo, criaremos uma classe SpeechPoweredAnimal que estende a classe.
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
Este é o mesmo padrão usado no ViewHolder. A classe RecyclerView.ViewHolder é um objeto absoluto.tracA classe `View` adiciona funcionalidades à View, assim como adicionamos o método `speak` aos animais. Essa funcionalidade adicional é o que permite que ela funcione corretamente ao lidar com o `RecyclerView`.
Eis como criaríamos um OneContactViewHolder a partir de um OneContactView:
//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)
O RecyclerView possui um adaptador que nos permite conectar nosso array de contatos ao ContactsView dentro do RecyclerView.
Adicionando uma visualização
O ViewGroup não é redesenhado 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 adicionarmos uma View a um ViewGroup, você verá as alterações 1 minuto depois, quando o ViewGroup for "atualizado".
RecyclerView.Recycler
Tarefa Básica nº 3 – Armazenamento em Cache
Um dos melhores exemplos de onde atualizamos a página regularmente é no navegador. Imaginemos, por exemplo, que o site que estamos visitando seja estático e não envie conteúdo dinamicamente; precisaríamos ficar atualizando a página 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 vermos novos tweets seria clicando no botão de atualizar para buscar novamente o conteúdo.
Obviamente, redesenhar a tela inteira é algo custoso. Imagine se tivéssemos uma largura de banda limitada com nossa operadora de celular e nossa lista de tweets tivesse muitas imagens e vídeos; seria caro baixar todo o conteúdo da página a cada atualização.
Precisaríamos de uma forma de armazenar os tweets já carregados e garantir que nossa próxima solicitação possa informar quais tweets já possui. Portanto, o sistema não baixa tudo novamente, obtendo apenas os tweets novos, e também verifica se algum tweet salvo localmente não existe mais, para que possa ser excluído. O que estamos descrevendo é chamado de cache.
As informações que enviamos ao site sobre o conteúdo que temos são chamadas de metadados. Então, na verdade, não estamos apenas dizendo "queremos carregar seu site", mas sim "queremos carregar seu site e aqui está parte do conteúdo que já salvamos da última vez que carregamos; por favor, use-o para nos enviar apenas o que ainda não está lá, para que não usemos muita largura de banda".
Chamadas de layout – A lista de tweets deve ser uma loucura
Um exemplo de chamada de layout é `scrollToPosition`. Este é um exemplo comum presente em aplicativos de bate-papo. Se alguém em uma conversa responde a uma mensagem anterior, alguns aplicativos incluem a resposta e um link para a mensagem original, que, ao ser clicado, leva você de volta ao local onde a mensagem original estava.
Caso chamemos esse método antes de adicionarmos um LayoutManager ao nosso RecyclerView e antes de termos um RecyclerView.Adapter, o scrollToPosition(n:Int) é simplesmente ignorado.
Comunicação entre componentes do RecyclerView
Etapa 4 – Retornos de chamada
O RecyclerView, em seu funcionamento, possui muitas partes móveis. Ele precisa lidar com o LayoutManager, que nos diz como organizar as Views, seja linearmente ou em uma grade. Ele também precisa lidar com um adaptador que converte nossos itens (contactList) em Views (OneContactView) e, em seguida, em ViewHolders (OneContactViewHolder) com os quais o RecyclerView pode trabalhar usando os métodos que fornece.
A matéria-prima para o RecyclerView são nossas Views, por exemplo, OneContactView, e uma fonte de dados.
contactList:Array<Contact>
Usamos um cenário simples como ponto de partida para entender o que o RecyclerView tenta realizar. Um caso básico, em que temos um array estático de 1000 contatos que queremos mostrar ao usuário, é fácil de compreender. O funcionamento do RecyclerView realmente começa a ganhar vida quando a lista deixa de ser estática. Com uma lista dinâmica, precisamos pensar no que acontece com a visualização na tela quando adicionamos ou removemos um item da lista.
RecyclerView.LayoutManager
Além de decidir como nossas visualizações são dispostas, seja linearmente ou em uma grade, o LayoutManager realiza muito trabalho nos bastidores para ajudar a organizar o layout.ping O reciclador sabe quando fazer a reciclagem.
É responsável por manterping tracO LayoutManager é responsável por informar ao sistema de reciclagem quais Views estão atualmente visíveis na tela, permitindo que sejam reutilizadas em vez de permanecerem lá consumindo memória ou serem destruídas para dar lugar a novas Views.
Isso significa que o LayoutManager precisa manter tracO algoritmo controla a posição do usuário enquanto ele rola a lista. Isso é feito através de uma lista de posições indexadas, ou seja, o primeiro item começa em 0 e aumenta até corresponder ao número de itens na lista.
Se pudermos visualizar 10 itens em nossa lista de, digamos, 100, inicialmente o LayoutManager estará ciente de que tem o foco nas visualizações View-0 até View-9. À medida que rolamos a tela, o LayoutManager consegue calcular quais visualizações perdem o foco.
O LayoutManager consegue liberar essas visualizações para o mecanismo de reciclagem, permitindo que sejam 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 cenário ideal se a lista fosse estática, mas um dos casos de uso mais comuns do RecyclerView é com listas dinâmicas, onde os dados podem vir de um endpoint online ou até mesmo de um sensor. Não apenas dados são adicionados, como também dados da lista são removidos ou atualizados.
O estado dinâmico dos nossos dados pode dificultar bastante a compreensão do comportamento do LayoutManager. Por esse motivo, o LayoutManager mantém sua própria lista de itens e posições, separada da lista usada pelo componente de reciclagem. Isso garante que ele execute sua função de layout corretamente.
Ao mesmo tempo, o LayoutManager do RecyclerView não quer representar incorretamente os dados que possui. Para funcionar corretamente, o LayoutManager sincroniza com o RecyclerView.Adapter em intervalos definidos (60 ms), compartilhando informações sobre os itens da nossa lista, ou seja, itens adicionados, atualizados, removidos ou movidos de uma posição para outra. O LayoutManager, ao receber essas informações, reorganiza o conteúdo na tela para refletir as alterações quando necessário.
Muitas das operações principais relacionadas ao RecyclerView giram em torno da comunicação entre o RecyclerView.LayoutManager e o RecyclerView.Adapter, que armazena nossas listas de dados, às vezes estáticos, às vezes dinâmicos.
Além disso, o RecyclerView nos apresenta métodos que podemos usar para monitorar eventos, como o onBindViewHolder, quando o RecyclerView.Adapter vincula o conteúdo da nossa lista (por exemplo, um contato) a um ViewHolder para que ele seja usado para exibir as informações na tela.
Outro exemplo é o onCreateViewHolder, que nos informa quando o RecyclerView.Adapter pega uma View comum, como OneContactView, e a converte em um item ViewHolder com o qual o RecyclerView pode trabalhar.
Além dos mecanismos principais que permitem a reciclagem, o RecyclerView oferece maneiras de personalizar o comportamento sem afetar a reciclagem. A reutilização de Views dificulta a realização de tarefas comuns que costumamos fazer com views estáticas, como reagir a eventos onClick.
Como sabemos, o RecyclerView.LayoutManager que apresenta as Views ao usuário pode, por um momento, ter uma lista de itens diferente da RecyclerView.Adapter que tem a lista armazenada em um banco de dados ou transmitida de uma fonte. Colocar eventos OnClick diretamente nas Views pode levar a comportamentos inesperados, como excluir o contato errado.
Gradle
Se quisermos usar o RecyclerView, precisamos adicioná-lo como uma dependência no nosso arquivo build.gradle. No exemplo a seguir, usamos a implementação “androidx.recyclerview:recyclerview:1.1.0”, que é a versão mais recente de acordo com este artigo.
Depois de adicionar a dependência ao nosso Gradle arquivo, seremos solicitados por Android Studio para sincronizar as alterações. É assim que nosso Gradle O arquivo ficará assim após a adição de um RecyclerView em um projeto vazio, utilizando apenas as configurações padrão.
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 29 buildToolsVersion "29.0.2" defaultConfig { applicationId "com.guru99.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" }
No momento, temos apenas um arquivo de layout. Vamos começar com um exemplo simples, onde usamos um RecyclerView para exibir uma lista de nomes de frutas na tela.
Lista de itens
Vamos navegar até o arquivo MainActivity e criar um array com os nomes das frutas, logo antes do método onCreate() que foi gerado durante a configuração.
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("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 essa lista na tela usando um RecyclerView. Para isso, navegaremos até o diretório de layouts que contém nossos layouts e criaremos uma View que será responsável por exibir uma fruta.
Layout a ser usado para cada item da nossa lista
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/fruitName" /> </TextView>
No TextView acima, adicionamos um campo id que será usado para identificar uma View. Ele não é gerado por padrão. Atribuímos ao 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 um arquivo de layout main_layout.xml que foi gerado automaticamente. Se escolhermos um projeto vazio, ele terá gerado um arquivo de layout. XML contendo um ConstraintLayout e, dentro dele, um TextView com o texto "Olá". Apagaremos todo o conteúdo e deixaremos o layout contendo apenas o RecyclerView, como abaixo:
<?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/fruitRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" />
Também adicionamos um atributo id para o RecyclerView, que usaremos para referenciá-lo em nosso código.
android:id="@+id/fruitRecyclerView"
Em seguida, voltaremos ao nosso arquivo MainActivity. Usando os IDs que criamos, poderemos referenciar as Views que acabamos de criar. Começaremos referenciando o RecyclerView usando o método findViewById() fornecido por AndroidFaremos isso em nosso método onCreate(). Nosso método onCreate() ficará assim:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
Crie um ViewHolder
Em seguida, criaremos um RecyclerView.ViewHolder, responsável por receber nossa View e convertê-la em um ViewHolder, que o RecyclerView usa para exibir nossos itens. Faremos isso logo após o método onCreate().
package com.guru99.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<String> = arrayOf<String>("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
Em seguida, criaremos uma classe `FruitArrayAdapter` que estende a classe `RecyclerView.Adapter`. O `FruitArrayAdapter` que criarmos será responsável por: obter os nomes das frutas do array de frutas, criar um `ViewHolder` usando nossa view `one_fruit_view.xml`, vincular as frutas ao `ViewHolder` e, em seguida, vincular dinamicamente o conteúdo à view que criamos.
package com.guru99.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<String> = arrayOf<String>("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<String>) : RecyclerView.Adapter<FruitViewHolder>() }
Android Studio Isso adicionará linhas onduladas vermelhas ao nosso FruitArrayAdapter, indicando que precisamos implementar métodos que o RecyclerView possa usar para conectar nosso array a um ViewHolder.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { 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 nossa matriz chamando a propriedade de tamanho da matriz.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { 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 `onCreateViewHolder`. É aqui que o `RecyclerView` nos pede para ajudá-lo a construir um `FruitViewHolder`. Se nos lembrarmos, nossa classe `FruitViewHolder` tinha esta aparência:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
É necessário criar nossa fruitView, que criamos como um arquivo XML chamado one_fruit_view.xml. Podemos criar uma referência a esse XML e convertê-la em uma View da seguinte forma.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { 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) { } }
A parte restante é a substituição do método onBindViewHolder.
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
O RecyclerView.Adapter solicita uma posição inteira, que usamos para buscar um item da nossa lista. Ele também nos fornece um contêiner (holder) para que possamos associar o item obtido do `fruitArray` à View contida dentro do contêiner. A View contida em um `ViewHolder` é acessível através do campo `ViewHolder.itemView`. Uma vez que obtemos a View, 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<TextView>(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<String>) : RecyclerView.Adapter<FruitViewHolder>() { 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<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } }
Finalmente, estamos prontos para conectar as partes restantes do nosso RecyclerView, que é criar um LayoutManager que dirá ao RecyclerView como exibir o conteúdo da lista, seja de forma linear usando o LinearLayoutManager ou em uma grade usando o GridLayoutManager ou o StaggeredGridLayoutManager.
Criar gerenciador de layout
Vamos voltar à 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 de `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.guru99.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<String> = arrayOf<String>("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<String>) : RecyclerView.Adapter<FruitViewHolder>() { 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<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } } }




