Tutorial: CRUD em Android com SQLite e RecyclerView (Parte 2)

Android SDK

Tutorial: CRUD em Android com SQLite e RecyclerView (Parte 2)

Luiz Duarte
Escrito por Luiz Duarte em 10/07/2017
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Seguimos agora com a tão aguardada segunda parte do Tutorial: CRUD em Android com SQLite e RecyclerView. Se chegou agora e ainda não fez a primeira parte do tutorial, faça, pois é bem difícil de conseguir acompanhar sem ter concluído todos os preparativos iniciais.

O que é uma RecyclerView? A RecyclerView é a substituta mais moderna ao ListView, que até então era o componente recomendado para criar listagens. No entanto, o ListView está atrelado à versão do SDK que você está rodando, é pesado e tem pouca flexibilidade. A RecyclerView é um pouco mais complicada de lidar mas vale a pena considerando os benefícios que se tem com ela, como veremos nesta segunda parte do tutorial.

Veremos neste tutorial:

  1. Criando e explorando o projeto Basic (parte 1)
  2. Criando a tela de cadastro/edição (parte 1)
  3. Preparando a persistência de dados (parte 1)
  4. Cadastrando Clientes (parte 1)
  5. Listando Clientes
  6. Atualizando Clientes
  7. Removendo Clientes

Mãos à obra!

Parte 5: Listando clientes

Primeiro, vamos criar na nossa classe ClienteDAO o método que vai retornar todos os clientes do banco de dados, como segue:

Aqui usamos o método rawQuery do objeto SQLiteDatabase para executar uma consulta SELECT em cima de todos clientes da base o que nos retorna um cursor. Com um laço em cima do curso conseguimos retornar cada uma das colunas em cada uma das linhas da tabela, populando uma coleção de Clientes com essas informações.

Atenção: para pegar o índice da coluna a partir do seu nome, o mesmo deve ser informado tal qual foi criado no CREATE TABLE original. Caso não queira pegar por nome, você pode pegar por índice, desde que saiba a ordem que as colunas serão retornadas do banco (se especificar as colunas ao invés de usar SELECT *, fica mais fácil).

Agora vamos voltar a nossa atenção ao arquivo de layout content_main.xml, que vamos editar para incluir uma lista de elementos nele, que mais tarde será populada com os clientes do banco de dados. Já que estamos usando de boas práticas em vários cantos, não usaremos aqui ListView, mas sim a versão mais moderna para listagem de elementos que é a RecyclerView, um componente gráfico independente de versão do SDK que é atualizado constantemente, tem muito mais desempenho, mas opções visuais e de animação, etc.

Para poder usar a RecyclerView, primeiro você deve adicionar algumas dependências no seu arquivo build.gradle dentro da pasta app, dentro da seção dependencies do mesmo:

Atenção: Certifique-se de que a versão das bibliotecas ‘-v7’ seja a mesma entre todos eles, no meu caso, 25.3.1, a mais recente na data que escrevo este post. É possível também que a dependência da biblioteca support:design já esteja adicionada nesse arquivo, nesse caso não precisa adicionar de novo.

Para podermos criar belas listas usando RecyclerView, devemos adotar uma técnica semelhante ao que fizemos no post sobre ListViews personalizadas: temos de ter um layout XML que represente um único item da lista, para o mesmo ser replicado.

Sendo assim, adicione um novo layout XML na pasta de layouts chamado item_lista.xml com o layout ConstraintLayout na raiz e crie uma interface semelhante à esta:

Item da Lista

O código XML para fazer esta interface está abaixo. Você não precisa fazer igual, mas atente aos ids dos componentes que é o mais importante no momento:

Agora sim, vamos para a content_main.xml, apagamos aquele Hello World que estava lá e vamos adicionar uma RecyclerView, que fica na categoria AppCompat da Palette do Layout Editor do Android Studio.

A RecyclerView possui uma propriedade listitem, onde devemos informar o layout XML que usaremos para os itens da mesma, no nosso caso, o item_lista.xml:

O preview da sua tela principal deve mudar também, logo após você adicionar a informação do layout da lista.

Para que os campos do item_lista.xml sejam mapeados corretamente quando carregarmos os elementos na lista, devemos criar uma classe Java extendendo ViewHolder, como a ClienteHolder.java abaixo:

Na sequência, vamos criar o ClienteAdapter.java que vai fazer a ligação entre os dados dos clientes e os campos do layout item_lista. Para a classe ser uma Adapter é necessário herdar RecyclerView.Adapter<ViewHolder> e implementar os métodos obrigatórios.

  • onCreateViewHolder(ViewGroup parent, int viewType): Método que deverá retornar layout criado pelo ViewHolder já inflado em uma view.
  • onBindViewHolder(ViewHolder holder, int position): Método que recebe o ViewHolder e a posição da lista. Aqui é recuperado o objeto da lista de Objetos pela posição e associado à ViewHolder. É onde a mágica acontece!
  • getItemCount(): Método que deverá retornar quantos itens há na lista. Aconselha-se verificar se a lista não está nula como no exemplo, pois ao tentar recuperar a quantidade da lista nula pode gerar um erro em tempo de execução (NullPointerException).

Vale ressaltar que os métodos onCreateViewHolder e onBindViewHolder não são chamados para todos os itens inicialmente, eles são chamados apenas para os itens visíveis para o usuário. Quando o usuário sobe e desce a lista, estes dois métodos são chamados novamente associando a view reciclada ao conteúdo daquela posição que agora será visível.

Agora é a hora de voltarmos ao nosso MainActivity.java e programar a integração da RecyclerView com os dados da ClienteDAO. Primeiro, crie um método configurarRecycler, como abaixo:

E depois, chame esse configurarRecycler() no final do onCreate da MainActivity.java, para que ele seja disparado e popule inicialmente a RecyclerView com a lista de clientes do banco.

Isso deve funcionar parcialmente. Quando abrimos o app pela primeira vez ele vai listar corretamente todos os clientes. Mas quando adicionamos um novo cliente, ele não se atualiza.

Até poderíamos resolver isso facilmente mandando chamar novamente o configurarRecycler() a cada nova inserção, mas essa não é a maneira correta de fazer. Ao invés disso, quando um cliente novo for inserido no banco, ele também deve ser inserido na coleção de clientes que o ClienteAdapter referencia, em memória. Para isso, vamos fazer alguns ajustes.

Primeiro, vamos criar um método no ClienteDAO.java para retornar o último Cliente inserido no banco de dados. Isso porque como o ID é automático e autoincremental, precisamos saber o ID que o cliente recebeu para adicionar o objeto completo na nossa lista (usaremos esse ID mais tarde na edição e exclusão). O código é bem parecido com o de retornarTodos, mais simples até, então dispensa explicações:

Agora vamos editar o nosso ClienteAdapter.java para ter um método que adicione novos clientes à lista:

Bem simples, apenas adiciona o cliente na coleção in-memory e notifica a RecyclerView que ela deve se atualizar.

Agora, no nosso click do botão de salvar, após salvar, vamos pegar o último elemento e adicionar ele no adapter usando o método que já está pronto nele, que inclusive atualiza o RecyclerView na sequência. Note que o trecho de código abaixo é o último bloco do onClick do btnSalvar e eu apenas coloquei duas linhas novas no início do if:

Com isso, as duas primeiras letras do CRUD estão prontas: o C de Create e o R de Read.

Lista funcionando

Agora é hora de fazer funcionar aqueles dois botões à direita dos nomes!

Curso React Native

Parte 6: Atualizando Clientes

Agora vamos programar a atualização de clientes. A ideia é a seguinte: o usuário clica no botão de editar, daí abrimos a mesma tela de cadastro, mas com os dados do cliente que está sendo editado já preenchido. Daí quando o usuário clica em Salvar, ele faz um update no banco ao invés de um insert.

Para fazer isso, primeiro vamos editar o nosso ClienteDAO para incluir uma sobrecarga do método salvar que recebe um id por parâmetro, informando que é uma atualização ao invés de uma inserção. Não apenas isso, mas achei mais interessante jogar fora aquele método salvar antigo e no lugar adicionei as duas versões abaixo (com e sem id):

Muito menor e mais inteligente!

Agora precisamos editar nossa classe ClienteAdapter.java, que é quem possui a lógica dos botões para programar o click do btnEditar. Primeiro, vamos começar adicionando este método que permite obter uma Activity a partir de uma View qualquer, algo útil considerando que o ClienteAdapter não é uma Activity:

Agora, ainda no ClienteAdapter.java, mas dentro do método onBindViewHolder, adicione o seguinte bloco de código no final do método:

Isso vai fazer com que, ao clicar no botão de editar de alguma das linhas da RecyclerView, seja disparado um “refresh” na Activity atual, passando o cliente que deve ser editado feito Extra.

Aproveitando que estamos aqui no ClienteAdapter.java, vamos adicionar um último método pra ele permitir a atualização fácil e rápida de clientes na RecyclerView mais tarde, no final desta etapa:

Apenas adicione o método acima na ClienteAdapter.java, que tem mecânica semelhante com outros método que também adicionamos por lá, e vamos continuar!

Voltando à MainActivity.java, adicione o seguinte bloco de código na classe:

Basicamente aqui temos uma variável que usaremos pra guardar o cliente que será editado e um método utilitário para selecionarmos o item de um Spinner a partir de seu valor, que usaremos mais tarde.

Agora, ainda no MainActivity.java, mas dentro do método onCreate, adicione esta verificação logo após o setContentView:

Isso faz com que, se estiver vindo um Cliente no Intent que disparou esta Activity, vamos abrir o app na tela de edição ao invés de listagem, já carregando os campos com os valores atuais do cliente.

Para finalizar, temos que fazer com que o botão de salvar seja mais inteligente do que atualmente é. Hoje ele sempre manda salvar um novo cliente, mas agora queremos que ele veja se estamos editando um novo cliente (através da variável clienteEditado) ou se estamos salvando um novo cliente. No código abaixo, eu apenas alterei poucas linhas dentro do onClick do btnSalvar, colocando um if onde antes era apenas uma chamada a dao.salvar:

Isso é o suficiente para fazer com que a nossa edição funcione tão bem quanto as demais operações do nosso app de CRUD.

Editando cliente

E com isso terminamos a letra U do nosso CRUD!

Parte 7: Removendo Clientes

Agora vamos programar a exclusão de clientes. A ideia é a seguinte: o usuário clica no botão de excluir, daí pedimos uma confirmação e, se ele confirmar, mandamos o ClienteDAO excluir o cliente e removemos o objeto do ClienteAdapter, notificando a lista para que seja atualizada.

Para fazer isso, primeiro vamos editar nossa classe ClienteDAO.java para que tenha um método de exclusão de Cliente:

Agora, vamos editar a classe ClienteAdapter.java para ter um método de exclusão lá também:

Aqui pegamos a posição do cliente que queremos excluir, removemos ele da lista e depois notificamos a RecyclerView.

Agora vamos juntar as pontas mexendo no onBindViewHolder do ClienteAdapter.java novamente, adicionando o listener ao click do btnExcluir presente nos itens da lista. Aqui temos de exibir um popup de confirmação para o usuário e, em caso afirmativo, realizar a exclusão de facto chamando os métodos que criamos anteriormente:

E o mais incrível, funciona!

Exclusão funcionando

E com isso terminamos a última letra do CRUD, a D de Delete!

Espero que tenham gostado deste mega tutorial. Deu uma trabalheira fazer mas tenho certeza de que ficou uma ótima referência pro pessoal que está querendo fazer um CRUD completo com boas práticas e componentes modernos.

Precisando de referências mais completas quanto ao uso do SQLiteDatabase, consulte a documentação oficial.

Tendo qualquer dúvida, chama aí nos comentários!

Curso Beholder

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

23 Replies to “Tutorial: CRUD em Android com SQLite e RecyclerView (Parte 2)”

Marcos

Olá Luiz, acompanho todas as suas postagens e já aprendi muito com seus tutoriais, continue com esse belo trabalho! Gostaria de saber como faço para mostrar além do nome dos clientes após o cadastro, o sexo,UF e se é vip ou não…abraço!

Luiz Fernando Jr

Boa noite Marcos. Primeiramente obrigado pelo feedback, fico feliz em estar ajudando. Sobre sua pergunta, basta adicionar mais TextViews (ou o componente que quiser) no template de item do RecyclerView e fazer o bind dele no ViewHolder e no Adapter como foi feito com o campo nome.

Marcos

Ok, entendi. Outra dúvida que tenho é em relação a atualização das edições, alguns clientes não atualizam após a mudança, sendo necessário uma nova ação(salvar novamente,inserir novo cliente) para que o mesmo mostre como ficou após as mudanças, como solucionar isso?

Luiz Fernando Jr

Sim, tu tem de atualizar a informação no Adapter e depois chamar o método de notify correspondente (que nem eu fiz com o notifyInserted) pra dizer para a Recycler qual a posição que mudou e que ela deve re-renderizar.

Raul Carlos de Mello

Oi Luiz. Exite um erro na sua lógica de programação. Conforme o Marcos comentou, ao atualizar um cliente, alguns não são atualizados, necessitando de uma nova operação de inserção com a opção de cancelar. Isto ocorre pois na MainActivity em btnSalvar você utliza – Cliente cliente = dao.retornaUltimo(); – que irá retornar o último campo inserido. Assim a atualização ocorrerá somente se for efetuado no último cliente da lista do RecyclerView.

Luiz Fernando Jr

Sim, conforme mencionei a ele em minha última resposta, tem de chamar o método notify apropriado, pois o notifyInserted tem de saber a posição que foi adicionada para para atualizar a Recycler (e no caso de update nem vai haver nova posição). Um exemplo é o notifyDataSetChanged, que manda re-renderizar toda RecyclerView, mas que deve ser usado somente em último caso, pois caso contrário perde o ganho de performance dela frente à ListView, mas tem outras opções. Assim que der tempo eu adiciono elas no código, atualizo os fontes, etc.

João Pedro

Cara, primeiramente, muito obrigado pelo tutorial, pelas minhas pesquisas foi o mais atualizado que encontrei e vai me ajudar muito no meu projeto de TCC. Você pode me explicar porquê utilizou essa estratégia de uma Activity central alterando o layout, ao invés de usar fragments?

Sou iniciante, mas fiquei curioso de qual seria a diferença das duas opções e inclusive encontrei um problema no app que eu acredito que poderia ser resolvido se fosse utilizado fragments.
O problema é o seguinte: Ao clicar no botão de editar em um item do RecyclerView, a MainActivity é carregada novamente, mas colocando como extra o objeto Cliente que será editado. Ao carregar a Activity novamente, é feito uma verificação no onCreate() se na intent que originou essa Activity tem o extra, e se sim, a Activity deixa visível apenas o layout de edição com os dados do cliente que será editado. A questão é que esse extra fica lá “pra sempre”, gerando problemas quando é feito um refresh na Activity que não seja ao clicar no botão de editar. Encontrei dois casos em que isso acontece: se o usuário tiver apertado no editar do usuário X, e depois tiver clicado em cancelar, esse extra vai ficar na memoria e ao mudar a orientação do aparelho, é dado um refresh na Activity, que vai fazer a verificação e encontrar o extra e entrar no layout de edição do usuário X, quando deveria entrar na tela de listagem inicial novamente (não alterar nada independente da orientação do aparelho), o segundo caso é se o usuário fizer a mesma coisa do começo do caso um, mas ao invés de mudar a orientação do aparelho, clicar no fab para criar um novo cliente. Ao fazer isso, entra novamente no layout de editar o usuário X.

Como eu disse, acredito que ao utilizar fragments, não teria esse problema, mas não tenho certeza. Sei também que tem como resolver isso sem fragments, apenas fazendo outras verificações, mas acho que seria mais fácil com fragments.

Enfim, posso estar falando besteira, por favor avalie minhas colocações e se os problemas que encontrei são realmente inerentes ao código do post ou se eu cometi algum erro ao copiar o mesmo. Também queria que você explicasse, como disse no início, qual seria a diferença prática e os possíveis benefícios de utilizar Fragments ou a sua estratégia de layouts, tanto para resolver os problemas que encontrei tanto para usabilidade em geral do código.

Luiz Fernando Jr

Boa noite João. Essa ideia que usei no post pareceu ser interessante quando comecei a escrevê-lo, mas realmente possui algumas falhas que devem ser contornadas e que acabaram não resultando em uma solução 100%. Era uma prova de conceito mesmo, você está corretíssimo em citar o uso de fragments como uma opção melhor. Um abraço.

João Pedro

Tudo bem. Foi bom que vi que aparentemente entendi o código de fato e não fiquei apenas passivamente vendo e copiando. Tentarei implementar com fragments e corrigir os dois problemas que encontrei e o que o amigo do comentário abaixo encontrou, porém mantendo sua estrutura geral (gostei muito dos Design Patterns que você usou, pode ser coisa básica mas pra mim que é iniciante e não conhece nenhum, vai ser ótimo como boa prática). Tem algum meio de comunicação mais pessoal com você onde posso mandar o link do código no GitHub quando concluir, para você dar uma olhada?

Luiz Fernando Jr

Na barra lateral esquerda você encontra todas minhas redes sociais e na página ‘Sobre’ tem um formulário de contato para me enviar emails.

Arlen Pereira

Boa noite Luiz, para completar o tutorial seria interessante adicionar a função de reordenar a listagem do RecyclerView através do “clicar + arrastar” o item. Seria possível? Pois estou quebrando a cabeça para entender como reordeno a lista do RecyclerView e gravo numa variável (ou diretamente no SQLite) para memorizar as alterações de sequencia dos itens, até ai tudo bem, o problema é quando insiro um novo item a lista deve manter a sequencia salva e colocar este ultima item no final da variável (ou no SQLite)… Obrigado pela atenção.

Luiz Fernando Jr

Boa noite Arlen. Este tutorial a princípio foi encerrado, mas pensarei a respeito de incluir esta funcionalidade em uma possível parte 3.

Christian de Carvalho

beleza! tentei seguir aqui mas ta dando erro no projeto, tem como disponibilizar os arquivos? valeu!

Luiz Fernando Jr

Preenche o formulário no final do post que você recebe os fontes por email.

Christian de Carvalho

eu preenchi ontem e até agora nao veio nada. se puder enviar agradeço.. christian.coliveira @gmail.com

Luiz Fernando Jr

Não achei seu email na minha base. Cadastrei você lá, agora deve ter recebido.

Adriano Braga Azevedo

Estou tentando receber os arquivos do projeto mas, não chega nenhum e-mail para o download dos mesmos.

Luiz Fernando Jr

Estranho, aparentemente está tudo ok com a automação que dispara os emails com os fontes. Não caiu na sua caixa de spam? Qual o seu email, quero ver se o sistema cadastrou com sucesso.

Adriano Braga Azevedo
Luiz Fernando Jr

Eu te cadastrei de novo e aparentemente agora o sistema diz que você recebeu. Me avisa se tiver qualquer problema.

Adriano Braga Azevedo

Desde ontem venho verificando tanto a caixa de entrada quanto a caixa de spam.

Saulo Calixto

Muito esclarecedor, consegui aplicar facilmente em minha aplicação! Grato!

Luiz Fernando Jr

Fico feliz que tenha sido útil!