Consumindo APIs em Android com Retrofit e Butter Knife – Parte 2

Esta é a segunda parte do tutorial de como consumir APIs em um app Android usando Retrofit e ButterKnife. Caso tenha caído aqui de pára-quedas, sugiro ler (e programar) a primeira parte do tutorial primeiro, seja clicando no link anterior ou usando o sumário logo abaixo (os itens 1 a 3 são da parte 1 do tutorial).

Apenas recapitulando, estou usando uma API escrita em Node.js com banco MySQL, cujo tutorial e fontes se encontram neste post. Esta API e seu banco estão hospedados na Umbler, que fornece MySQL gratuito para pequenos projetos.

Sobre as bibliotecas-foco desse tutorial, a Retrofit permite abstrair requisições HTTP a APIs usando classes Java de uma maneira muito elegante e produtiva. Enquanto o Butter Knife permite fazer o binding de recursos do seu app (componentes visuais, imagens, cores, strings, eventos, etc) com muito menos código do que normalmente é necessário.

E pra encerrar a introdução, a belíssima tela (#sqn) do nosso app está como abaixo:

Tela do app
Tela do app

Dito isso, veremos neste tutorial completo os seguintes itens (do 1 ao 3 já foram vistos na parte 1):

  1. Preparando a API
  2. Criando o app
  3. Usando ButterKnife
  4. Usando Retrofit
  5. Fazendo tudo funcionar
  6. Indo além!

Vamos lá!

#4 – Usando Retrofit

Como mencionado anteriormente, Retrofit é um HTTPClient que agiliza bastante algumas tarefas tediosas de mapear APIs HTTP em objetos Java para tornar as requisições mais type-safe.

Neste tutorial, temos uma API REST de clientes que possui algumas operações elementares que precisamos mapear para métodos e classes Java, o que o Retrofit vai nos permitir fazer muito facilmente. Mas antes de sair usando ele, precisamos adicioná-lo como uma dependência em nosso build.gradle (no Module: app), na seção dependencies (tal qual fizemos com o ButterKnife):

Aqui, eu adicionei o Retrofit e o converter que vai nos permitir serializar e desserializar JSON usando a biblioteca Gson (isso porque minha API trabalha com JSON, isso pode variar no seu caso). Apenas atente que o converter utilizado deve ser da mesma versão do seu Retrofit (2.3.0 no meu caso).

E no arquivo proguard-rules.pro, onde tem as configurações do Proguard, adicione as seguintes linhas (recomendação do dev do projeto Retrofit):

Atenção: se você não sabe o que é o Proguard, ele é um otimizador, minificador, ofuscador e pré-verificador de código Java que roda automaticamente segundo uma série de regras para tornar o APK final o menor possível (no caso do Android) e o mais “seguro” possível, do ponto de vista de ofuscação de código. Mais informações nesta resposta do Quora.

Com isso temos o Retrofit configurado em nosso projeto e pronto para usar. O primeiro passo é adicionar uma classe Java que representa um cliente do seu banco de dados, o que eu fiz abaixo do jeito mais simples possível:

Atenção: foi usada aqui a mesma capitalização (maiúsculas e minúsculas) das colunas da tabela Clientes do meu banco de dados MySQL. Ajuste de acordo com o seu cenário. Caso os nomes não coincidam, a desserialização não acontecerá corretamente mais tarde e os métodos retornarão clientes “vazios”.

Agora, adicione uma interface Java ao nosso projeto, que representará a API localmente. Aqui eu chamei de ClientesService.java:

Para cada endpoint da nossa API (falei sobre isso na parte 1 deste tutorial, lembra?), criamos um método na interface usando annotations para os verbos HTTP e para os parâmetros que devam vir no path e no body da requisição HTTP.

Para quem entende um mínimo de web APIs, o código é auto-explicativo.

Atenção: meu método que retorna cliente por id está retornando uma lista de clientes propositalmente porque na minha API Node+MySQL eu sempre retorno um array JSON em GETs. Outro ponto de atenção são os métodos de insert e update que possuem a annotation @FormUrlEncoded pois minha API Node+MySQL espera chave-valor no body (caso esperasse JSON, eu usaria um @Body Cliente como parâmetro). Métodos com esta annotation devem possuir parâmetros com @Field indicando o nome de cada chave que será enviada no body. Para saber os fields corretos, consulte a documentação da API (no meu caso, consulte o tutorial original).

Agora, para podermos usar esta interface, vamos adicionar uma nova variável e um método de carregamento dessa variável em nossa MainActivity.java:

Esse método de carregamento deve ter sua baseUrl apontada para a sua URL (no meu caso, node.luiztools.com.br). Caso queira apontar para uma webapi rodando localhost, use 10.0.2.2 como URL (e inclua a porta, se necessário). Além disso, esse método usa a biblioteca Gson para converter o body das requisições para JSON, conforme exigido pela minha API (isso pode variar na sua).

E no onCreate da MainActivity, adicione uma chamada à esse método logo no final (aproveitei para adicionar a permissão de uso de rede na thread principal, conforme explico melhor aqui):

Com isso, sempre que precisarmos consumir nosso webservice, basta chamar MainActivity.api.nomeDoMetodo e é isso que temos de fazer agora, substituindo aqueles testes fake por chamadas de verdade à API.

Atenção: como usaremos webapis através da Internet do smartphone, devemos ter permissão para tanto. Adiciona no AndroidManifest.xml as duas linhas logo no início, dentro da tag manifest:

Isso resolve para smartphones com Android anterior ao 6. Para smartphones Marshmallow (6) em diante, você deverá incluir um código de verificação e solicitação de permissão do usuário a cada requisição. Eu incluirei esse código nas chamadas dos métodos abaixo, então não se preocupe.

#5 – Fazendo tudo funcionar

Agora que temos tudo pronto com nosso Retrofit, é hora de usar nosso recém-criado ClientesService através da variável api nos métodos de click dos nosso botões.

Vamos começar com o btnBuscarOnClick, que agora deverá ficar assim:

Note que houve uma imensa alteração aqui. Logo no início eu defino uma constante que define o número 1 para uma ação de busca, isso vai ser útil na sequência. Antes de fazer qualquer coisa no clique deste botão eu verifico se o app tem permissão de usar a Internet, caso contrário, solicita ao usuário que dê a referida permissão, passando o código desta ação de busca (1).

Caso ele tenha a permissão, o código flui naturalmente, usando o método da interface que retorna apenas um cliente por id e usando o cliente retornado para popular os campos.

Para que isto funcione, temos de sobrescrever o método onRequestPermissionsResult que é executado automaticamente após a permissão ter sido concedida ou não pelo usuário:

Aqui é onde usamos o código da ação de busca (1), pois todos os pedidos de permissão vão cair aqui e precisamos saber qual método executar após a permissão ser concedida. Essa permissão será requisitada apenas na primeira ação, nas subsequentes o fluxo cairá no else do código anterior a este e tudo acontecerá naturalmente pois a permissão fica salva.

Sugiro já realizar um teste aqui, para garantir que está funcionando. Acesse a API Node+MySQL no seu navegador e mande listar todos os clientes para pegar o ID de algum deles e usar no app para buscar.

Próximo passo, btnExcluirOnClick!

Aqui as mudanças não foram tão drásticas se você considerar que muita coisa é repetida em relação às permissões e à própria chamada da api em si, que é apenas um método pois o Retrofit faz todo o trabalho pela gente.

Note que defini uma nova constante para ACTION_EXCLUIR, constante essa que devemos incluir no switch/case do onRequestPermissionsResult:

Próximo passo, btnSalvarOnClick!

Neste método, que guarda muitas semelhanças com os anteriores, executamos o método correto da interface conforme for um insert ou update, no mais não há novidade alguma.

Não esqueça de incluir a nova constante ACTION_SALVAR no onRequestPermissionsResult:

Quando for testar o botão de salvar você receberá as mensagens de sucesso ou de fracasso, mas para ter certeza de que realmente funcionou, acesse sua API para ver se o cliente em questão foi alterado ou se apareceu um novo cliente, dependendo do seu caso.

#6 – Indo além!

Opcionalmente existem diversas melhorias que você pode fazer neste app.

Alguns apps usam o banco local (SQLite) para fazer cache dos dados da API que o app consome. Você aprende a usar o banco local neste post. Isso pode ser uma boa ideia dependendo do seu caso, apenas tomando cuidado de ter uma forma de atualizar o seu cache ou mesmo expirá-lo automaticamente.

Não criamos uma tela de listagem de todos os clientes neste tutorial. Isso geralmente é feito e a recomendação atual é usar RecyclerView para isto, um componente moderno e poderoso que permite criar belas e rápidas listagens. Neste post eu ensino como tirar um bom proveito do RecyclerView e com poucas adaptações você consegue usar neste tutorial aqui.

Não adicionei validações no formulário de cadastro/edição de cliente. Isso é super importante para garantir que somente dados íntegros sejam enviados à API. Facilmente você nota que se clicar diretamente em Salvar, sem preencher nenhum campo, mesmo assim um novo cliente vazio é cadastrado com um novo id.

Caso a API possuísse autenticação (a minha é aberta), existem algumas configurações adicionais no Retrofit para que ele envie junto o cabeçalho Authorization. Isso é melhor explicado na seção de Header Manipulation da documentação do Retrofit.

E com isso encerramos este tutorial de como consumir APIs em Android usando Retrofit e ButterKnife.

Espero que tenha gostado e que lhe seja útil!

Curtiu o post? Então dá uma olhada no meu livro de Android clicando no banner abaixo pra aprender a criar outros tantos apps incríveis!

Criando apps para empresas com Android

Consumindo APIs em Android com Retrofit e Butter Knife

Esses dias eu estava lendo umas postagens no excelente grupo de programadores Android do Facebook e me deparei com um comentário de um dev que reclamava que os tutoriais na Internet geralmente ensinavam do jeito mais difícil, sem usar bibliotecas que facilitam o trabalho do desenvolvedor e que são muito utilizadas pelas empresas no dia-a-dia dos projetos. Dentre as bibliotecas citadas por ele que deveriam ser ensinadas estavam Retrofit e ButterKnife.

Sinceramente, eu não usava nenhuma das duas até então. Retrofit já estava no meu radar há algum tempo, mas não havia tido a necessidade de usá-la, pois sua utilidade é ser um HTTP Client fortemente tipado para Java e Android, permitindo abstrair APIs em código Java muito facilmente.

Já o ButterKnife eu nunca tinha ouvido falar, mas realmente parece muito útil pois ele permite fazer o binding automático dos componentes visuais em variáveis Java locais, para uso na sua lógica de aplicação. Imagino que deva ter algumas limitações, mas resolvi testar mesmo assim.

O resultado desses estudos originaram este tutorial, onde vou criar um app que consome uma API REST escrita em Node.js e hospedada na Umbler, para fornecer um cadastro de clientes para o usuário.

Veremos neste tutorial completo:

  1. Preparando a API
  2. Criando o app
  3. Usando ButterKnife
  4. Usando Retrofit (em breve)
  5. Fazendo tudo funcionar (em breve)
  6. Indo além! (em breve)

Vamos lá!

Atenção: se este é o primeiro app Android que programa em sua vida, procure fazer este tutorial primeiro.

#1 – Preparando a API

Usarei neste tutorial uma API Node.js que fornece um CRUD sobre uma base de clientes em um MySQL. Se você quiser fazer a API do zero e aproveitar para aprender um pouco de Node.js, esse post aqui ensina tudo que você precisa saber.

Mesmo que não queira aprender a programar Node.js, sugiro acessar o respectivo post, seguir as instruções para criação do banco, baixar o zip do projeto e testar usando o POSTMAN, assim como eu ensino no tutorial (não esqueça de entrar no index.js e editar os dados da conexão MySQL para apontar para o seu banco).

Basicamente usaremos neste tutorial os endpoints abaixo, que você deve testar e se certificar que está funcionando antes de avançar para a próxima etapa:

  • GET /clientes – lista todos os clientes;
  • GET /clientes/{id} – retorna apenas um cliente;
  • POST /clientes – salva um novo cliente (dados no body);
  • PATCH /clientes/{id} – atualizar um cliente existente (dados no body);
  • DELETE /clientes/{id} – exclui um cliente;

O app em si não será muito elaborado, pois o foco é o uso das bibliotecas Retrofit e ButterKnife, mas sim, usaremos todos os endpoints supracitados.

Atencão: não aponte para nenhuma URL que apareça em meus exemplos ou códigos-fonte, pois são bancos e sites de exemplo que geralmente não ficam no ar por muito tempo. Crie a sua infra de teste na sua hospedagem de site, para usar os SEUS endpoints.

Após se certificar de que sua API está funcionando localmente, sugiro hospedá-la em um provedor de hospedagem. Na verdade eu sempre crio os bancos de dados em provedores de hospedagem também, pois não gosto de ter vários servidores de banco instalados em minha máquina consumindo recursos. Particularmente para hospedar Node.js e MySQL eu recomendo a Umbler, pois inclusive bancos MySQL pequenos são de graça e o deploy pode ser feito via Git, como mostro nesse vídeo.

Com a API 100%, vamos em frente!

#2 – Criando o APP

Como faremos uma prova de conceito, esse app não será nem um pouco elaborado. Na parte final deste tutorial darei algumas ideias de como deixá-lo profissional, mas por ora, vamos fazer um app de uma tela só.

Nesta tela poderemos fazer a pesquisa de cliente por id, o cadastro, a atualização e a exclusão de clientes. Para ver se tudo está sendo persistido no banco de dados, usaremos o endpoint “GET /clientes” no navegador mesmo, para que sejam listados todos os clientes e possamos ver se está tudo funcionando como deveria.

Crie um novo projeto no Android Studio (mas pode ser Eclipse ou NetBeans também, com pequenas variações nos passos que vou mostrar) usando o template de Activity Basic, aquele que vem com um botão flutuante no canto inferior direito. Usarei aqui o ConstraintLayout, que já expliquei como funciona em outro post e o nome da minha activity principal é MainActivity.

O Android Studio gera dois arquivos de layout para essa Activity, um chamado activity_main.xml que serve como “arcabouço” ou “esqueleto” para o layout, e o content_main.xml que serve como “miolo” ou  “área interna” do layout. Isso permite uma organização maior do layout bem como reuso de código. Focaremos aqui na construção da interface usando o content_main.xml, que por padrão vem um Hello World que deve ser apagado.

Arraste para esse formulário um campo de edição de texto (id:txtId) com dois botões à sua direita (id:btnBuscar e id:btnExcluir). Logo abaixo, coloque um campo de edição de texto para o nome do cliente (id:txtNome) e outro mais abaixo para o CPF do cliente (id:txtCPF). No final do formulário, coloque um botão de salvar (id:btnSalvar). Crie as constraints necessárias (ou use o recurso Infer Constraints) para que sua tela se pareça com a minha abaixo:

Tela do app
Tela do app

Caso tenha dificuldade, use e/ou consulte os fontes abaixo, do content_main.xml:

Alguns pontos que merecem atenção nesse layout são:

  • os campos de edição de texto possuem “hints”, o que remove a necessidade de usar TextViews para rótulos de campos;
  • os campos de edição de texto possuem inputType, para agilizar a digitação dos valores já exibindo o teclado virtual correto para cada um;

Já na activity_main.xml, fiz duas alterações bem pequenas: troquei o ícone do floating button e troquei o id dele (de fab para btnNovo). O código XML apenas desse componente que foi alterado está abaixo:

Na sequência vamos fazer esta tela funcionar, mesmo que de maneira incompleta usando ButterKnife!

#3 – Usando ButterKnife

Agora que temos a nossa tela pronta, é hora de programarmos ela. Usaremos a biblioteca open-source ButterKnife para nos auxiliar com tarefas repetitivas e muito chatas que são os bindings dos widgets dos layouts XML em variáveis Java locais.

Caso ainda não tenha entendido o ganho que isso traz, imagina que a seguinte linha de código:

é substituída por:

Visualmente pode não parecer grande coisa, mas esta declaração é feita no topo da classe da Activity, o que permite que esse componente seja usado em toda a classe. Além disso, essa biblioteca é extremamente performática pois não usa Reflection, mas sim um transpilador das anotações para código Java nativo de binding que não serve apenas para binding de campos mas também de cores, strings, imagens, etc.

Mas antes de sairmos aproveitando tudo o que a biblioteca tem a oferecer, precisamos adicionar a sua dependência ao nosso projeto, modificando nosso arquivo build.gradle (do Module:app) para adicionar as seguintes linhas dentro da seção dependencies:

Essas linhas adicionam uma nova dependência ao projeto bem como um processador de annotations (aquelas palavras precedidas por @ que fica sobre os métodos e variáveis).

Com essa dependência adicionada, vá na sua MainActivity.java e adicione as seguintes variáveis no escopo da sua classe, fora do onCreate:

O tipo EditText irá exigir a adição de imports na sua classe, bem como a annotation @BindView, apenas use o recurso ALT+ENTER e siga em frente.

No onCreate desta Activity, logo abaixo o setContentView, você deve inicializar o ButterKnife para que ele faça as conversões corretas de código (aproveite para jogar fora o código velho do FloatingActionButton):

E é aqui que a magia acontece. Com muito menos repetição de código, os meus componentes estarão carregados nas variáveis locais que defini no trecho de código anterior à esse, permitindo o seu uso sem qualquer receio nos métodos de onClick dos botões, os quais faremos na sequência, também usando ButterKnife pra fazer o binding do método ao componente apropriado (quem já fez isso com Listeners vai ver muito valor aqui).

Primeiro, vamos programar o btnNovoOnClick (o floating button) para apenas limpar todos os campos do formulário, incluindo o id, sinalizando um novo cadastro (apenas coloque esse método no escopo da classe MainActivity.java):

Note a annotation @OnClick acima do método, que informa qual o id do componente que queremos mapear o click. Você pode inclusive colocar diversos ids para o mesmo método tratar, pode omitir o parâmetro View no método (como eu fiz) ou mesmo substituir o view pelo tipo correto do componente (pra evitar castings no código, embora eles ainda ocorram no código compilado).

Na sequência, vamos programar o btnBuscarOnClick: ele deve pegar o id que foi digitado pelo usuário e usar esse id como filtro para trazer os dados do usuário e preencher o formulário. Aqui não iremos na API ainda, então faremos uma falsa busca por enquanto:

Quase finalizando, vamos programar, também de maneira fake, o click do nosso botão de excluir. A ideia aqui é que o usuário digite o id e clique em excluir, o app pede uma confirmação e avisa se conseguiu excluir ou não:

E por fim, o click do botão de salvar. Esse botão também é um pouquinho mais “esperto” que os outros, pois ele deve diferenciar o cadastro de um novo cliente de uma edição de um cliente já existente. Isso é feito analisando o campo id, que deve estar vazio ou com um número inferior a 1.

Se você mandar este app executar agora e fazer alguns testes, verá os comportamentos esperados uma vez que ainda não estamos consumindo a API, o que faremos na próxima parte deste tutorial usando Retrofit!

E aí, gostou da primeira parte? A parte 2 você confere neste link!

Enquanto isso, dá uma olhada no meu livro de Android clicando no banner abaixo pra aprender a criar outros tantos apps incríveis!

Criando apps para empresas com Android

Como fazer um app de cronômetro em Android

Primeiramente, a pergunta que você deve estar se fazendo: por que eu iria querer fazer um app de cronômetro?

Um app de cronômetro, genérico, não tem lá muita serventia uma vez que já existem soluções semelhantes. Mas pense que, dentro de um contexto e com as modificações adequadas, um app de cronômetro, por exemplo, pode virar um app de corrida, que registra as suas metas e os tempos alcançados. Pode virar um app de musculação, para ajudar a controlar o tempo de descanso entre as séries. Pode virar um app de natação e outros esportes de resistência, para medir o seu progresso (resistência, apnéia, etc).

Ou seja, eu te ensino como programar o app de cronômetro em Android, mas o que você vai fazer com esse conhecimento, aí fica por conta da sua criatividade e veia empreendedora!

Requisitos: para conseguir acompanhar este tutorial você já deve saber usar componentes de tela, saber programar Java, ter o Android Studio instalado na sua máquina e funcionando (outras opções menos poderosas são NetBeans e Eclipse) e saber usar RecyclerView. Use os links fornecidos para mais informações ou aprenda tudo isso e muito mais em meu livro de Android.

Vamos ver neste tutorial:

  1. Criando o projeto
  2. Tela do cronômetro
  3. Tela do histórico
  4. Funcionando

Vamos lá!

#1 – Criando e configurando o projeto

Crie um novo projeto no Android Studio com o nome de Cronometro. Quando ele pedir para criar a MainActivity, escolha o template Tabbed Activity e na tela seguinte, selecione a opção Action Bar Tabs (with View Pager).

Tabbed Activity
Tabbed Activity

Isso irá criar para você toda a lógica e estrutura básica para ter aquela experiência de uma tela só, com abas que você pode acessar tanto clicando no nome da mesma como deslizando pro lado com o dedo (swipe). Todo esse funcionamento já vem pronto neste template de Activity, apenas temos de personalizar o fragment principal que vem de exemplo de acordo com as nossas necessidades de tela e de pois criar o segundo fragment pra tela de histórico das “cronometradas”. Faremos isso mais tarde.

Primeiro, vamos ajustar os títulos e quantidade de abas.

Fazer isso é bem simples, abra a classe MainActivity.java e procure por uma classe interna chamada SectionsPagerAdapter. Dentro dela você vai encontrar um método getCount() que deve retornar 2 ao invés de 3, e um método getPageTitle com o switch/case que define os títulos das abas, mude para Cronômetro e Histórico, apagando o excedente.

A sua SectionsPagerAdapter deve ficar da seguinte forma:

Se você executar esse projeto agora, já verá que ele possui algumas funcionalidades toscas, como mudar o texto central conforme a aba e a navegação entre as abas funciona com swipe e com o toque no nome da mesma.

Mas vamos adiante!

#2 – Tela do cronômetro

Agora vamos personalizar nossa primeira tela.

activity_main.xml é o template padrão para todas telas, então remova o Floating Button desse arquivo de layout, pois não queremos que ele apareça em todas telas, mas apenas na principal.

No fragment_main.xml, que representa a primeira view da nossa interface com abas, a view do cronômetro, devemos ter um TextView com o tempo do cronômetro correndo e um botão para iniciar/parar o mesmo (você pode pensar em mais botões se quiser). Como na tela abaixo:

Tela Cronômetro
Tela Cronômetro

A ideia é a seguinte: o usuário aperta o Play (id:fab), o botão vira Stop e o tempo começa a correr (id:section_label). Quando ele tocar no botão de Stop, ele vira Play novamente, o tempo pára e é registrado na outra tela que lista os tempos realizados (tela de histórico).

Sendo assim, o fragment_main.xml abaixo dá conta do recado:

Para fazer funcionar essa tela não é muito complicado. Mas também não é muito simples, tem de fazer direito senão não funciona!

Dentro da MainActivity você vai encontrar uma classe estática interna chamada PlaceholderFragment, vamos começar declarando algumas variáveis e constantes novas dentro dela:

Os tipos de algumas variáveis não serão reconhecidos. Apenas use o ALT+ENTER para adicionar os imports necessários no topo do seu projeto. A variável section_label é o display numérico do nosso cronômetro. A variável initialTime será usada mais tarde para calcular o tempo que deve ser exibido no display. Falarei do Handler mais tarde, as constantes falam por si só e o isRunning é uma flag que indica se o cronômetro está “correndo” ou não.

Na sequência, devemos criar um objeto Runnable (logo abaixo das variáveis anteriores), que nada mais é do que um objeto que executará um trecho de código (run()) em uma thread separada. Isso porque não podemos travar a thread da UI com nosso código que atualiza o display do cronômetro. Usar um Runnable é uma boa opção quando você precisa rodar algum código que não trave a tela e a variável Handler que declaramos anteriormente permitirá fazer a “ponte” do Runnable com a UI.

O run() da nossa thread é bem simples, apenas pega o tempo de execução atual, menos o tempo em que o start foi dado para descobrir há quanto tempo o cronômetro está correndo. Com essa informação, imprime no formato hh:mm no display e manda o handler avisar a tela da alteração.

Agora, vamos criar o método que configura o botão para que funcione no modelo start/stop que queremos. Coloque este método na mesma classe PlaceholderFragment que estamos customizando até o momento:

Neste método eu carreguei o display (section_label),  o handler, o botão start/stop (fab) e diss que quando ele for clicado faremos um teste: se o cronômetro estiver parado (!isRunning) nós iniciamos ele e mudamos seu ícone para o stop (eu usei um pause nativo do Android, mas pode usar uma imagem se quiser).

Caso o cronômetro já esteja correndo, ele pára, reseta o display e o ícone volta a ser o start.

E por fim, para que a tela seja carregada corretamente e os componentes sejam configurados (através do método que acabamos de criar), precisamos configurar o método onCreateView que já existe na classe PlaceholderFragment:

Esse método é chamado para criar cada uma das abas da nossa aplicação, sendo cada aba um fragment de layout, por isso que faço o teste pra saber se estou na aba inicial para configurar o cronômetro. Note que referenciei R.layout.fragment_history, que ainda não existe. Crie ele na pasta res/layout como um layout XML qualquer, usaremos ele mais tarde.

Teste sua aplicação, ela já deve estar funcionando como um cronômetro bem simples. Com poucos ajustes no objeto Runnable você consegue adicionar centésimos e milésimos de segundo, caso queira.

#3 – Tela do Histórico

Caso não tenha feito ainda, crie um layout XML chamado fragment_history.xml na pasta res/layout.

Vamos dizer que você esteja treinando para uma prova de resistência. Cada vez que você treina, deseja registrar quanto tempo conseguiu aguentar correndo, pedalando ou nadando, por exemplo. Esse registro é útil para ver se está tendo progresso na sua atividade física, pois em teoria, os tempos devem ser cada vez maiores. Ou o contrário, caso sejam treinos de velocidade.

Sendo assim, na tela de histórico vamos colocar uma lista com todas as execuções do cronômetro, registrando a data da execução e o tempo que o cronômetro marcava quando foi pausado.

Vamos usar aqui uma RecyclerView, que já usamos neste outro post. Primeiro, vamos criar um layout chamado history_item.xml que será o template utilizado para cada um dos itens do histórico:

O que resulta neste layout aqui:

history_item.xml
history_item.xml

Atenção aos ids utilizados: tempo para o texto maior, com o tempo que o cronômetro marcava quando foi parado e data, com a ocasião em que ele foi parado.

Agora que temos o template de um item da lista, vamos adicionar um RecyclerView ao nosso fragment_history.xml, definindo que ela deve usar o history_item.xml como template para seus itens. Nosso fragment_history.xml deve ficar assim:

Agora é a hora de programarmos para essa RecyclerView funcionar!

Vamos começar criando uma classe HistoryHolder.java para mapear os campos do history_item.xml para variáveis locais:

Com essa classe criada, vamos criar outra classe, desta vez a classe History, que vai representar um objeto do histórico (simplifiquei bastante aqui, pode usar tipos mais interessantes que String para os dois atributos):

E por fim, a classe que faz a união da HistoryHolder com a History: a HistoryAdapter!

Este adapter é bem simples de entender:

  1. no construtor esperamos a lista de objetos History que queremos listar;
  2. o método addHistory deve ser chamado sempre que a gente quiser adicionar um novo item na lista. Ele sempre adiciona no topo (últimos primeiros) e manda a lista se renderizar novamente;
  3. no onCreateViewHolder nós carregamos a classe HistoryHolder que criamos anteriormente;
  4. no onBindViewHolder nós fazemos o de-para do objeto History para o HistoryHolder (que por sua vez joga as informações no hostory_item.xml);
  5. por fim, o getItemCount() apenas retorna quantos elementos History nossa lista possui.

Para finalizar a exibição desta tela, vamos adicionar uma nova variável e um novo método na classe estática PlaceholderFragment que fica dentro da MainActivity.java:

A variável adapter foi declarada fora do método para que seja acessível por outros métodos mais tarde. Esse método setupHistory configura a RecyclerView, definindo que qualquer alteração no adapter deve repercutir na aparência da lista. Note que inicializei o adapter com um ArrayList vazio. Não vou entrar aqui em detalhes sobre persistência de dados local ou remota, coisa que já fiz em outros posts, consulte os links. Neste tutorial de hoje usarei apenas objetos em memória mesmo, o que quer dizer que se fechar o aplicativo e rodar de novo, ele vai iniciar zerado.

Agora apenas devemos chamar este método no onCreateView desta classe PlaceholderFragment. Temos de apenas mudar o trecho abaixo para incluir um else:

Isso é o suficiente para que a tela de histórico apareça vazia nos seus testes no Android Studio. No entanto, precisamos fazer a “conexão” entre ambas telas para que, cada vez que um novo tempo seja marcado no cronômetro, ele seja registrado nessa lista.

#4 – Funcionando

Para fazer a tela do cronômetro se comunicar com a tela de histórico é bem simples, apenas temos de utilizar a variável adapter que deixamos a nível de classe.

Quando você pausa o cronômetro, deve registrar as informações daquela execução e adicionar no adapter, como abaixo (edite o código presente no onClick do FloatingActionButton da PlaceholderFragment, por via das dúvidas incluí o método inteiro de setup do botão):

Apenas duas linhas foram alteradas nesse método inteiro, exatamente as duas logo após meu comentário “atualiza a recyclerView”. Aqui eu instancio um SimpleDateFormat pra definir o formato de data que aparecerá na interface e adiciono um novo histórico com o tempo que aparecia no display do cronômetro quando o mesmo foi pausado, bem como a data atual para registro.

Se você executar agora esse aplicativo no simulador e mandar rodar o cronômetro algumas vezes, quando você acessa a aba Histórico (seja tocando no nome da aba ou deslizando a tela pra direita) você deve ver algo parecido com abaixo:

RecyclerView Funcionando
RecyclerView Funcionando

E isso é tudo pelo tutorial de hoje.

Claro, existem inúmeras coisas que você pode fazer, sendo a mais óbvia salvar os dados na nuvem ou localmente no app. Mas tem outras mudanças que podem ser legais, desde personalizar essa interface bem simples, registrar os centésimos de segundo, criar gráficos de performance do atleta (uma feature paga!), incluir recursos de geolocalização pro atleta saber onde estava naquele treino ou até mesmo para calcular a distância percorrida (no caso de um cronômetro de corrida, caminhada ou ciclismo), entre muitas outras coisas.

Isso tudo eu deixo para você e sua criatividade!

Curtiu o post? Então dá uma olhada no meu livro de Android clicando no banner abaixo pra aprender a criar outros tantos apps incríveis!

Criando apps para empresas com Android