Alura > Cursos de Mobile > Cursos de Android > Conteúdos de Android > Primeiras aulas do curso Recycler View parte 2: Listeners, animações e boas práticas

Recycler View parte 2: Listeners, animações e boas práticas

Implementando o listener - Introdução

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á?

Implementando o listener - Adicionando listener para cada elemento

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.

lista de notas

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.

mensagem de toast

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 configuraAdpatercriaremos 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".

nova mensagem de toast

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.

Implementando o listener - Recebendo a nota no listener

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.

notas numeradas: "Título 1", "Título 2" até "Título 9" em sequência

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.

notas responsivas

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.

Sobre o curso Recycler View parte 2: Listeners, animações e boas práticas

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:

Aprenda Android acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas