Olá!
Sou Alex Felipe, instrutor da Alura. Apresentarei a você a segunda parte do curso de RecyclerView. Para prosseguir, é muito importante que você tenha feito a primeira parte do curso, pois ele irá te proporcionar os conhecimentos necessários para avançarmos no projeto e adicionar funcionalidades.
De acordo com o pedido de nosso cliente para uma aplicação, inserimos uma lista de notas, como "Compras", "Estudar". O usuário clica sobre as categorias e pode escrever conteúdos. O cliente fez mais algumas requisições: ao tocar em uma nota e acessar uma nota e modificar seu conteúdo, essa modificação deve ser salva e exibida na parte principal da lista. A princípio para algo simples, mas veremos que o Recicle View possui certas peculiaridades para que essa ação possa ser realizada, isto é, o listener de cada um dos elementos.
Além disso, o cliente solicitou outra feature interesse: a capacidade de remover itens da lista utilizando um efeito de swipe ou deslizamento com o dedo. Uma vez que um elemento da lista tiver sido eliminado, os outros se reordenarão de forma automática.
A aplicação deve, ainda, fornecer ao usuário a possibilidade de ordenar os elementos da lista de acordo com a sua escolha. Suponhamos que elas sejam organizadas por meio de prioridades diferente, portanto as notas podem ocupar espaços hierárquicos na lista. Basta que o usuário pressione e arraste a nota para a posição de interesse.
Por fim, modificaremos o aspecto visual da aplicação e inseriremos o tema solicitado pelo cliente.
Implementaremos todas essas funcionalidades, conhecendo novas técnicas de utilização do Recicle View. Trabalharemos com refatoração, boas práticas de código e no Android.
Vamos lá?
A primeira feature esperada pelo cliente é a de alteração da nota. Ao tocar em uma nota da lista, o formulário deverá ser aberto e conteúdo da nota exibido. As modificações realizadas devem ser salvas e exibidas na lista.
No Android Studio, no arquivo ListaNotaActivity
analisaremos como usar um listener para cada um dos elementos. Nos cursos iniciais, quando trabalhávamos com list view , tínhamos um listener próprio para cada um dos elementos, o chamado setOnItemClickListener()
.
Se começamos a escrever a referência do RecyclerView, a listaNotas
e tentarmos inserir um listener setOnItemClickListener()
, não haverá implementação disponível, contudo, se escrevermos algo que se inicie com setonclick
, a implementação é apresentada.
A implementação apresentada é aquela comum para todas as views, o que não é o comportamento que desejamos neste caso, não queremos que o click seja feito para o Recicle View e, sim, para cada elemento dele.
Por padrão, o RecyclerView não possui esse tipo de implementação. Contudo, ao analisarmos proposta do RecyclerView, o Adapter para ser mais específico, notaremos a existência dos viewholders, que representa justamente as views a partir de uma referência itemView
. Portanto, se possuímos uma referência de view, temos a capacidade de implementar o listener dentro da viewholder.
Iremos até ListaNotasAdapter
e acessaremos a NotaViewHolder
, onde encontraremos a referência de view itemView
, o que nos possibilitará a implementação do listener, uma vez que toda as views possuem a implementação de quicklistener.
Escreveremos itemView.setOnItemClickListener(new View.OnClickListener()
para realizar a implementação
@Override
public int getItemCount() { return notas.size(); }
class NotaViewHolder extends RecyclerView.ViewHolder {
private final TextView titulo;
private final TextView descricao;
public NotaViewHolder(View itemView) {
super(itemView);
titulo = itemView.findViewById(R.id.item_nota_titulo);
descricao = itemView.findViewById(R.id.item_nota_descricao);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view){
}
Apenas para realizar um teste, inseriremos um Toast
, com o textoViewHolder clicado
.
@Override
public int getItemCount() { return notas.size(); }
class NotaViewHolder extends RecyclerView.ViewHolder {
private final TextView titulo;
private final TextView descricao;
public NotaViewHolder(View itemView) {
super(itemView);
titulo = itemView.findViewById(R.id.item_nota_titulo);
descricao = itemView.findViewById(R.id.item_nota_descricao);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view){
Toast.makeText(context, text"ViewHolder clicado", Toast.LENGTH_SHORT).show();
}
Iremos testar a aplicação para verificar se a implementação foi bem sucedida. Na aplicação, clicaremos sobre uma nota na lista e veremos nossa mensagem surgir na parte inferior da tela.
Dentro de OnClick
podemos inserir uma série de comportamentos desejados, por exemplo a abertura de formulários. Contudo, há um detalhe que devemos nos atentar: por mais que possamos realizar as implementações via OnClick
, elas ficarão bem limitadas, isto é, o clique sempre terá um mesmo comportamento, o que não é ideal se quisermos ter uma maior diversidade de ações.
Precisaremos criar uma interface que permitirá a implementação de um clique de elemento, no caso, um clique itemListener
. Dessa forma teremos um comportamento similar ao listView
.
Faremos essa implementação dentro de recyclerview.adapter
, denominada OnItemClickListener
, dessa forma mantemos o padrão da listView
.
package br.com.alura.ceep.ui.recyclerview.adpater;
/**
*Created by Alura Preto on 19/02/2018.
*/
public interface OnClickListener {
}
Apagaremos os conteúdos comentados e incluiremos o comportamento esperado para a interface, no caso, clique de item. Escreveremos uma assinatura de void
e onItemClick()
package br.com.alura.ceep.ui.recyclerview.adpater;
public interface OnClickListener {
void onItemClick();
}
Dentro de ListaNotasAdapter
, precisamos criar uma referência ao OnItemClickListener
. Faremos isso por meio de um atributo de classe, isto é private OnItemClickListener onItemClickListener
public class ListaNotasAdapter extends ReclyverView.Adapter<ListaNotasAdapter.NotaViewHolder> {
private final List<Nota> notas;
private final Context context;
private OnItemClickListener onItemClickListener;
Com a referência criada, temos a capacidade de executar a assinatura que criamos e executar a implementação para qualquer cliente que a chamar. Removeremos o Toast
e delegaremos a implementação para quem realiza o chamado, isto é : onItemClickListener.onItemClick()
.
@Override
public int getItemCount() { return notas.size(); }
class NotaViewHolder extends RecyclerView.ViewHolder {
private final TextView titulo;
private final TextView descricao;
public NotaViewHolder(View itemView) {
super(itemView);
titulo = itemView.findViewById(R.id.item_nota_titulo);
descricao = itemView.findViewById(R.id.item_nota_descricao);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view){
onItemClickListener.onItemClick();
}
Com a implementação realizada, precisaremos fazer com que ela seja possível para quem realiza a chamada. Ao checarmos ListaNotasActivity
a partir do configuraAdpater
criaremos essa possibilidade.
A principio não temos nenhum recurso que permita a implementação, ou seja, precisamos incluir uns setter.
private void configuraAdapter(List<Nota> todasNotas, RecyclerView listaNotas) {
adapter = new ListaNotasAdapter(context:this, todasNotas);
listaNotas.setAdapter(adapter);
}
Em ListaNotasAdapter
inseriremos o setter para onItemClickListener
.
public ListaNotasAdapter(Context context, List<Nota> notas){
this.context = context;
this.notas = notas;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
De volta ao ListaNotasActivity
teremos a capacidade de gerar setOnItemClickListener()
, e realizar a implementação new OnItemClickListener
.
private void configuraAdapter(List<Nota> todasNotas, RecyclerView listaNotas) {
adapter = new ListaNotasAdapter(context:this, todasNotas);
listaNotas.setAdapter(adapter);
adapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick() {
}
Neste ponto, conseguimos incluir o comportamento que desejamos. Não estamos vinculando ao ListaNotasAdapter
toda a responsabilidade de declarar qual é a ação, e sim, estamos delegando para quem chama a ação a identificação das necessidades dessa mesma ação.
Faremos um pequeno neste e incluiremos um Toast
e o texto ViewHolder na Activity
.
private void configuraAdapter(List<Nota> todasNotas, RecyclerView listaNotas) {
adapter = new ListaNotasAdapter(context:this, todasNotas);
listaNotas.setAdapter(adapter);
adapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick() {
Toast.makeText(context: ListaNotasActivity.this, text"viewHolder na Activity", Toast.LENGTH_SHORT).show();
}
Em nosso simulador Android inseriremos uma nota de exemplo com o título "nota de teste" e conteúdo "descrição de teste".
Agora temos a capacidade de realizar a ação que desejamos, como por exemplo, abrir um formulário ou qualquer outra. Não estaremos vinculados à apenas uma implementação, pois incluímos o OnItemClickListener
dentro de NotaViewHolder
.
Nas próximas aulas daremos continuidade à implementação do cliente.
Antes de darmos continuidade na implementação de alterar uma nota, faremos alguns ajustes:
Todas as vezes que vamos realizar um teste criávamos uma nota nova, para facilitar o processo criaremos algumas notas de teste. Faremos, ainda, outra modificação que faremos diz respeito à implementação que realizamos dentro do pacote recyclerview.adapter
, criaremos um pacote específico para listeners.
Observemos a implementação:
package br.com.alura.ceep.ui.recyclerview.adapter;
public interace OnItemClickListener {
void onItemClick();
}
Modificaremos adapter
para listener
. Contudo, ao observarmos mais atentamente a função da implementação, veremos que se trata de um listener para o adapter, logo podemos ser mais específicos escrevendo: package br.com.alura.ceep.ui.recyclerview.adapter.listener
.
package br.com.alura.ceep.ui.recyclerview.adapter.listener;
public interace OnItemClickListener {
void onItemClick();
}
Pressionaremos as teclas "Alt + Enter" e escolheremos a opção "Move to package", em seguida escolheremos o diretório ...\app\src\main\java\br\com\alura\ceep\ui\recyclerview\adaoter\listener
, que é local onde está o código Java.
Com essa alteração, alguns erros podem ter ocorrido, portanto verificaremos as classes do listener para averiguar se não houve nenhum tipo de impacto. Começaremos por ListaNotasAdapter
.
Notaremos um erro de compilação, afinal anteriormente o listener estava dentro do de ListaNotasAdapter
, e não precisava ser importado. Agora temos um panorama diferente, o listener está acessível dentro de um sub-pacote, ou seja, precisaremos importá-lo.
import br.com.alura.ceep.R;
import br.com.alura.ceep.model.Nota;
import br.com.alura.ceep.ui.recycerview.adapter.listener.OnItemClickListener;
Verificaremos ListaNotasActivity
, e encontraremos mais um erro de compilação em OnItemClickListener()
.
private void confguraAdapter(list<Nota> todasNotas, RecyclerView);
adaptee = new ListaNotasAdapter(context this, todasNotas);
listaNotas.setAdapter(adapter);
adapter.setOnItemClickListener(new OnItemClickListener() {
<****!****>
Isso ocorreu porque ainda está mantida a referência antiga:
import br.com.alura.ceep.ui.recyclerview.adapter.OnItemClickListener;
Modificaremos para:
import br.com.alura.ceep.ui.recyclerview.adapter.listener.OnItemClickListener;
Resolvemos as questões do pacote. Agora incluiremos as notas de teste para avaliar o funcionamento da aplicação de forma mais rápida e prática.
Dentro de ListaNotasActivity
temos pegaTodasNotas
, o dao
que faz uma instância e a chamada do método todos()
, que devolve todas as notas dentro do banco de dados simulado.
private List<Nota> pegaTodasNotas() {
notaDAO dao = new NotaDAO();
return dao.todos();
}
Antes de pegarmos todas as notas, podemos realizar a inserção as notas que desejamos. Incluiremos um for
, acompanhado de (int i = 0; i < 10; i++)
. Assim feito, utilizaremos a referência do dao
e chamaremos o método insere()
, que por sua vez receberá as notas e seus títulos, concatenaremos com i
. Queremos títulos do 0 ao 9, portanto precisamos fazer uma operação de soma: i + 1
.
private List<Nota> pegaTodasNotas() {
notaDAO dao = new NotaDAO();
for (int i =0. i < 10; i++){
dao.insere(new Nota(titulo:"Título " + i+1));
return dao.todos();
}
Contudo, quando a operação é realizada dessa maneira o 1
fique como uma string, e não seja somado por meio de uma operação aritmética, portanto precisamos inserir parênteses: (i + 1)
.
Feito isso, inseriremos também uma descrição
private List<Nota> pegaTodasNotas() {
notaDAO dao = new NotaDAO();
for (int i =0. i < 10; i++){
dao.insere(new Nota(titulo:"Título " + (i+1), descricao:"Descrição " + (i + 1)));
return dao.todos();
}
Pressionaremos o atalho "Alt + Shift + F10", e testaremos as modificações em nosso simulador Android. Veremos que as notas estão numeradas, como esperávamos. O nosso próximo objetivo é fazer com que a nota reaja ao clique dado pelo usuário, de forma que ele passa arrastá-la.
Entretanto, em nossa implementação onItemClickListener
, não há qualquer referência à nota.
Nosso primeiro passo é modificar a interface para que onItemClickListener
receba uma Nota
.
package br.com.alura.ceep.ui.recyclerview.adapter.listener;
import br.com.alura.ceep.model.Nota;
public interface OnItemClickListener {
void onItemClick(Nota nota);
}
No momento em que realizamos essa modificação, todos os membros que fazem uso da interface irão parar de compilar, por isso precisamos alterar as respectivas referências.
Acessaremos ListaNotasAdapter
e veremos que de fato a compilação foi interrompida, precisamos enviar a nota
de alguma maneira para resolver o problema.
private final TextView titulo;
private final TextView descricao;
public NotaViewHolder(View itemView) {
super(itemView);
titulo = itemView.findViewById(R.id.item_nota_titulo);
descricao = itemView.findViewById(R.id.item_nota_descricao);
itemView.setOnClickListener((view) -> {
onItemClickListener.onItemClick();
});
}
No atual panorama estamos implementando o clique dentro do construtor, pois vimos que isso já o suficiente e não precisamos realizar o processo de bind, afinal todas as views são atendidas.
Da mesma maneira que fizemos para criar o listener, podemos criar uma atributo de nota a dentro de NotaViewHolder
: em onItemClickListener.onItemClicl()
adicionaremos nota
, que será de NotaViewHolder
.
private final TextView titulo;
private final TextView descricao;
private Nota nota;
public NotaViewHolder(View itemView) {
super(itemView);
titulo = itemView.findViewById(R.id.item_nota_titulo);
descricao = itemView.findViewById(R.id.item_nota_descricao);
itemView.setOnClickListener((view) -> {
onItemClickListener.onItemClick(nota);
});
}
A principio nota
será uma referência nula. Agora, toda a responsabilidade que teremos é: toda a vez que formos fazer o processo de vinculação, deveremos incluir também a nota
. Portanto em vincula
adicionaremos this.nota = nota
, isto é, a referência de atributo que temos em NotaViewHolder
:
public void vincula(Nota nota){
this.nota = nota;
preencheCampo(nota);
}
Em ListaNotasActivity
receberemos essa nota, logo escreveremos em onItemClick
a referência Nota nota
, pois ela está sendo enviada no listener do NotaViewHolder
.
@Override
public void onItemClick (Nota nota) {
Toast.makeText( context: ListaNotasActivity.this,
text:"viewHolder na Activity",
Toast.LENGHT_SHORT).show();
}
Agora que temos a nota
, faremos uma impressão desse dado, e escreveremos nota.getTitulo()
, desse modo será exibida a informação da nota que foi clicada e teremos certeza se recebemos a nota conforme o clique do elemento.
@Override
public void onItemClick (Nota nota) {
Toast.makeText( context: ListaNotasActivity.this,
nota.getTitulo(),
Toast.LENGHT_SHORT).show();
}
Feitas as modificações, ao acessarmos nosso simulador Android e clicamos sobre uma nota, veremos a mesanagem que corresponde à nota, isto é, ao clicarmos em "Titulo 2", a mensagem na parte inferior da tela será "Título 2", e assim por diante.
Em outras palavras, conseguimos implementar o listener que nos permite inserir o clique em cada elemento, de forma que ele nos devolva o elemento que desejamos, no caso a nota. O próximo passo é fazer a implementação que enviará essa nota para o formulário para, então, conseguimos preencher os campos e realizar modificações.
O curso Recycler View parte 2: Listeners, animações e boas práticas possui 141 minutos de vídeos, em um total de 40 atividades. Gostou? Conheça nossos outros cursos de Android em Mobile, ou leia nossos artigos de Mobile.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.
Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.
Emitimos certificados para atestar que você finalizou nossos cursos e formações.
Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.
Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.
Emitimos certificados para atestar que você finalizou nossos cursos e formações.
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com Luri até 100 mensagens por semana.
Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.
Acesso completo
durante 1 ano
Estude 24h/dia
onde e quando quiser
Novos cursos
todas as semanas