Uploaded by Geraldo Ribeiro

Google Android - Uma abordagem prática e didática

advertisement
Google Android
Uma abordagem prática e didática
Rafael Guimarães Sakurai
Esse livro está à venda em http://leanpub.com/google-android
Essa versão foi publicada em 2018-03-02
Esse é um livro Leanpub. A Leanpub dá poderes aos autores e editores a partir do processo de
Publicação Lean. Publicação Lean é a ação de publicar um ebook em desenvolvimento com
ferramentas leves e muitas iterações para conseguir feedbacks dos leitores, pivotar até que você
tenha o livro ideal e então conseguir tração.
© 2015 - 2018 Rafael Guimarães Sakurai
Conteúdo
1. Olá Android! . . . . . . . . . . . . . . . . . . . . . .
1.1
Criando um novo projeto no Android Studio
1.2
Executando um aplicativo Android . . . . . .
1.3
Personalizando o aplicativo OlaAndroid . . .
1.4
Resumo . . . . . . . . . . . . . . . . . . . . .
1.5
Exercícios . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
2
10
19
28
28
2. Utilizando LinearLayout, TextView e Button
2.1
Criando o layout da tela da Calculadora
2.2
Adicionando ação aos botões . . . . . .
2.3
Resumo . . . . . . . . . . . . . . . . . .
2.4
Exercícios . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
30
30
36
40
41
3. Utilizando ImageView e AlertDialog . . . . .
3.1
Criando layout da tela do Jogo da Velha
3.2
Implementando as regras do jogo . . . .
3.3
Resumo . . . . . . . . . . . . . . . . . .
3.4
Exercícios . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
47
55
55
4. Requisitando outra tela com Intent . . .
4.1
Criando uma tela de autenticação
4.2
Criando a tela da lista de tarefas .
4.3
Usando o Intent . . . . . . . . . .
4.4
Resumo . . . . . . . . . . . . . . .
4.5
Exercícios . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
56
56
60
64
69
69
5. Trocando dados entre telas via Bundle . . . . . .
5.1
Bundle . . . . . . . . . . . . . . . . . . . .
5.2
Criando o projeto de Valet . . . . . . . . .
5.3
Adicionando a tela de cadastro de veículos
5.4
Chamando a tela de cadastro de veículo . .
5.5
Obtendo o retorno da chamada da Activity
5.6
Removendo os elementos da lista . . . . . .
5.7
Resumo . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
70
70
71
78
81
83
86
88
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTEÚDO
5.8
Exercícios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6. Persistindo dados com SQLite . . . . . . . . .
6.1
Montando o banco de dados no SQLite .
6.2
Adicionando a persistência do veículos
6.3
Resumo . . . . . . . . . . . . . . . . . .
6.4
Exercícios . . . . . . . . . . . . . . . .
.
.
.
.
.
89
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 90
. 92
. 96
. 100
. 100
7. ListView personalizada . . . . . . . . . . . . . . .
7.1
Criando o layout da activity_main . . . . .
7.2
Criando o layout da ListView . . . . . . . .
7.3
Adicionando o comportamento da ListView
7.4
Resumo . . . . . . . . . . . . . . . . . . . .
7.5
Exercícios . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
101
103
105
108
112
113
8. Chamando Web Service REST . . . . . . . . . . . . .
8.1
Introdução sobre Web Service REST . . . . . .
8.2
Alterando o aplicativo Meus Produtos . . . . .
8.3
Criando a tela de cadastro de produto . . . . .
8.4
Adicionando arquivos no emulador do Android
8.5
Integração com Web Service REST . . . . . . .
8.6
Resumo . . . . . . . . . . . . . . . . . . . . . .
8.7
Exercícios . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
114
114
115
119
123
125
143
143
9. Action Bar . . . . . . . . . . . . . . . . . . . . . . . .
9.1
Configurando o Action Bar . . . . . . . . . . .
9.2
Criando o layout das telas . . . . . . . . . . . .
9.3
Implementando o comportamento do aplicativo
9.4
Alterando o ícone do aplicativo . . . . . . . . .
9.5
Resumo . . . . . . . . . . . . . . . . . . . . . .
9.6
Exercícios . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
144
145
147
154
169
169
169
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
Java Developer Kit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Android Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Instalando o Android Studio no Windows . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
170
170
178
181
1. Olá Android!
Vamos criar uma aplicativo inicial, no estilo Olá Mundo! para Android. O objetivo é apresentar
como criar um projeto Android e utilizar uma Activity para controlar a tela e os componentes
TextView, ImageView e Toast.
O aplicativo terá um texto Olá Android!!! e a figura do logo do Android, também será adicionado
uma ação na figura que ao clicar nela aparece uma mensagem Olá!. No final, o aplicativo ficará
conforme apresentado na Figura 1.1.
Figura 1.1 - Aplicativo Olá Android!
Para desenvolvimento dos aplicativos Android utilizaremos a IDE Android Studio¹.
¹http://developer.android.com/sdk/index.html
2
Olá Android!
1.1 Criando um novo projeto no Android Studio
Para criar um projeto, selecione a opção Start a new Android Studio project (Iniciar um novo
projeto Android Studio) na tela de bem vindo ao Android Studio, como apresentado na Figura
1.1.1.
Figura 1.1.1 - Android Studio.
Na tela Create Android Project, apresentada na Figura 1.1.2, configuramos as informações
básicas do projeto, como:
• Application name (Nome do aplicativo): OlaAndroid;
• Company Domain (Domínio da empresa): seudominio.com.br, exemplo: googleandroid.leanpub.com
ou se preferir livro.capitulo1;
• Project location (Localização do projeto): Caminho do projeto no seu computador;
3
Olá Android!
Figura 1.1.2 - Criando um novo projeto Android.
Continuando, a tela Target Android Devices (Dispositivos alvo do Android), apresentada na
Figura 1.1.3, permite escolher qual será o foco principal do aplicativo, que pode ser Celular e
Tablet; Wear (como o relógio); TV; Android Auto (uso em automóveis); e para Android Things.
4
Olá Android!
Figura 1.1.3 - Escolhendo o dispositivo e versão mínima do Android.
Nesse exemplo, escolha a opção Phone and Tablet, também defina qual será a API mínima para
poder executar o aplicativo, como a API 18: Android 4.3 (Jelly Bean).
Clicando em Next, vamos continuar a criação do projeto. Na tela Add an Activity to Mobile
(Adicionar uma Activity ao aplicativo móvel), apresentada na Figura 1.1.4, podemos informar
se queremos criar uma Activity inicial para o projeto, de modo geral uma Activity é usada para
realizar a ações de uma ou mais telas do aplicativo.
Neste capítulo abordaremos apenas o básico para criar e iniciar um projeto, nos demais
capítulos apresentamos outros modelos de Activities.
5
Olá Android!
Figura 1.1.4 - Adicionando uma Activity ao projeto.
Marque a opção Empty Activity (Activity em branco) e clique em Next para prosseguir. Essa opção
cria uma Activity e tela com um texto de exemplo, algo bem simples que a partir dele é possível
continuar a implementação de diversos aplicativos.
Na tela Configure Activity (Configurar Activity), apresentada na Figura 1.1.5, temos a opção
de customizar os dados da Activity inicial que será criada no projeto. As informações que temos são:
• Activity Name (Nome da Activity): MainActivity
• Layout Name (Nome do layout): activity_main
A Activity representa o nome da classe que será criada para controlar as ações e atributos do layout
da tela; e o Layout é um arquivo XML que possui a estrutura e componentes da tela.
6
Olá Android!
Figura 1.1.5 - Configurando a Activity inicial do projeto.
Na primeira vez que um projeto Android é criado, pode ser necessário fazer alguns downloads, como
o apresentado na Figura 1.1.6.
7
Olá Android!
Figura 1.1.6 - Atualizações para o projeto Android.
Se após criar o projeto, aparecer uma mensagem de erro, como o da Figura 1.1.7, normalmente é
porque tem mais alguma atualização necessária para compilar o projeto.
8
Olá Android!
Figura 1.1.7 - Atualizações necessárias para compilar o projeto Android.
Então clique no link que automaticamente inicia as atualizações necessárias, como mostrado na
Figura 1.1.8.
9
Olá Android!
Figura 1.1.8 - Atualizações necessárias para compilar o projeto Android.
Após a criação do projeto e demais atualizações, o Android Studio abre uma visualização completa
da estrutura do projeto, como mostrado na Figura 1.1.9.
10
Olá Android!
Figura 1.1.9 - Projeto aberto no Android Studio.
1.2 Executando um aplicativo Android
Para executar um aplicativo Android podemos utilizar um hardware físico como celular, tablet, etc,
ou um emulador que simula o comportamento do aplicativo no dispositivo.
Na barra de tarefa do Android Studio (Figura 1.2.1), temos a opção de executar o aplicativo
clicando botão verde de Play ou usando o atalho CRTL + R.
Figura 1.2.1 - Executar projeto Android.
A tela Select Deployment Target (Selecione o alvo da publicação), apresentada na Figura 1.2.2,
será aberta para escolher se deve executar o aplicativo no dispositivo (conectado via USB) ou no
emulador.
Se for utilizar seu smartphone ou tablet para testar os exemplos, ative no dispositivo
a Depuração de USB, está opção está disponível em Configuração → Opções do
11
Olá Android!
desenvolvedor. Isso é algo que inicialmente vem escondido, mas na Internet você
encontra facilmente como ativar as Opções do desenvolvedor de acordo com o seu
dispositivo.
Na figura a seguir, meu smartphone está conectado no computador e não tenho nenhum emuladores
criado.
Se for utilizar seu dispositivo, selecione ele e click em OK para publicar este aplicativo diretamente
no seu dispositivo. Para utilizar os emuladores do Android, clique no botão Create New Virtual
Device (Criar novo dispositivo virtual).
Figura 1.2.2 - Escolhendo um dispositivo virtual - emulador.
Ma tela Virtual Device Configuration (Configuração virtual de dispositivos), apresentada na
Figura 1.2.3, há as categorias do emulador como TV, Phone, Wear e Tablet, e para cada categoria
temos a opção de usar as configurações de um hardware existente, como: Nexus 5, Nexus One, etc,
ou um formato de acordo com o tamanho da tela 5.4”, 4”, etc.
A escolha da configuração do emulador deixa definir o tipo de hardware (tamanho, resolução e
densidade) que será o foco do aplicativo (mais adiante no livro será apresentado o que significa cada
uma dessas características e como criar um aplicativo que funcione de modo agradável em várias
resoluções). Por agora você pode criar um emulador com base no hardware do Nexus 5X ou criar
um emulador que simule o comportamento do seu smartphone.
12
Olá Android!
Figura 1.2.3 - Escolhendo o hardware do novo dispositivo virtual.
No segundo passo precisamos informar qual a System Image (Imagem do Sistema), apresentado
na Figura 1.2.4, será utilizado no emulador, nesse caso a versão do Android.
13
Olá Android!
Figura 1.2.4 - Escolhendo a imagem (versão do Android) do dispositivo virtual.
Neste exemplo, não há nenhuma imagem do Android, por tanto aparece a opção para fazer
o download. Nessa tela há três categorias de imagens: Recommended (Recomendado) são as
versões mais recentes do Android, x86 Images (Imagens x86) são outras versões do Android para
arquitetura de hardware x86_64 e Other images (Outras imagens) são versões do Android para
outros hardwares (normalmente antigos) como: armeabi, arm64 e mips, essas versões são mais lentas
e podem ser utilizadas caso o seu computador não suporte as versões mais recentes com x86 ou
x86_64.
A instalação apresentada aqui será do Oreo, API Level: 26, ABI: x86 e Target: Android 8.0 (Google
Play), mas se preferir a instalação de outra versão é muito similar ao passo a passo apresentado a
seguir.
Sugiro não fazer download de todas as versões, cada uma tem em média 1GB de
tamanho.
Quando for fazer o download de uma nova imagem do Android é apresentado a tela License
Agreement (Contrato de Licença) (Figura 1.2.5), leia o contrato e se aceitar clique em Accept
(Aceitar) e Next (Próximo) para continuar.
14
Olá Android!
Figura 1.2.5 - Aceitando a licença de uso do Android.
A tela Component Installer (Instalação dos componentes) (Figura 1.2.6) apresenta os componentes instalados desta nova imagem do Android. Clique em Finish (Finalizar) para terminar a
instalação desta versão.
15
Olá Android!
Figura 1.2.6 - Finalizando a instalação da imagem para o emulador.
Voltando para a tela System Image (Imagem do Sistema) (Figura 1.2.7), selecione a imagem que
será utilizada nesse emulador e clica em Next (Próximo) para continuar.
16
Olá Android!
Figura 1.2.7 - Escolhendo a imagem do emulador.
E, por fim, revisamos as configurações do emulador, como apresentado na Figura 1.2.8:
•
•
•
•
O nome do AVD;
Dispositivo que será simulado;
Versão do Android;
Escala e orientação.
17
Olá Android!
Figura 1.2.8 - Revisando as configurações do dispositivo virtual.
Clique em Finish (Finalizar) para terminar a criação do emulador e pode fechar a tela do Android
Virtual Device Manager.
Voltando para a tela Select Deployment Target (Selecione o alvo da publicação) (Figura 1.2.9),
escolha o emulador que será iniciado e clique em OK.
18
Olá Android!
Figura 1.2.9 - Iniciando um emulador.
O emulador será iniciado, desbloqueie a tela e o aplicativo OlaAndroid será publicado e automaticamente iniciado, como apresentado na Figura 1.2.10.
19
Olá Android!
Figura 1.2.10 - Aplicativo iniciado no emulador.
Dica: O emulador demora um pouco para iniciar, então enquanto estiver desenvolvendo
mantenha o emulador ligado.
1.3 Personalizando o aplicativo OlaAndroid
Voltando ao Android Studio, vamos alterar o aplicativo para ficar um pouco mais interessante.
Na estrutura do projeto temos alguns pacotes que são de uso frequente como:
•
•
•
•
•
•
app → manifests - O arquivo AndroidManifest.xml contém a configuração do projeto;
app → java - Estrutura de pacotes com códigos fonte e testes do aplicativo;
app → res - Pacote de recursos;
app → res → drawable – Pacote com as imagens de acordo com a resolução;
app → res → layout – Arquivos no formato .xml com a estrutura do layout das telas;
app → res → values – Pasta dimens.xml com configurações de dimensões e tamanho
de fontes que podem ser reutilizados em todo projeto; o arquivo strings.xml com os textos
strings; e o arquivo styles.xml com o estilo de telas do aplicativo.
20
Olá Android!
A Figura 1.3.1 mostra a estrutura atual do projeto.
Figura 1.3.1 - Estrutura do projeto.
Para editar a tela do aplicativo, abra o arquivo app → res → layout → activity_main.xml.
A edição de uma tela pode ser feita de modo Design (Figura 1.3.2) adicionando os componentes
por meio da paleta e configurando pelas propriedades.
21
Olá Android!
Figura 1.3.2 - Edição da tela no modo Design.
Também podemos alterar o código da tela editando o arquivo activity_main.xml no modo Text
(Figura 1.3.3).
22
Olá Android!
Figura 1.3.3 - Edição da tela no modo Text.
Note que em ambos modos, também temos mais opções para configurar o layout que é renderizado,
podemos escolher o tipo de aparelho, se o aparelho está na vertical ou horizontal, o tema do
aplicativo, a classe associada com essa tela, a localização para trabalhar com internacionalização
de textos e a versão do Android utilizada quando for renderizar a tela do layout, como apresentado
na Figura 1.3.4.
Figura 1.3.4 - Opções de preview do aplicativo.
No activity_main.xml altere apenas o código do TextView para:
Olá Android!
1
2
3
4
5
6
7
8
9
23
<TextView
android:id="@+id/texto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:gravity="center"
android:text="@string/ola_android"
android:textSize="40sp"
app:layout_constraintTop_toTopOf="parent" />
Vai aparecer um erro na linha do android:text, mas não se preocupe que já corrigiremos.
O TextView representa um texto na tela, como um label (rótulo), no qual pode ser definido o seu
texto, como será o seu layout, tamanho do texto, cor do texto, entre outros.
Esse TextView está utilizando uma string (texto) chamada ola_android, para criar o conteúdo desse
texto, na estrutura do projeto abra o arquivo ** app → res → values → strings.xml** e adicione
mais uma linha para a string ola_android e conteúdo Olá Android!!!.
O arquivo strings.xml ficará com o seguinte conteúdo:
1
2
3
4
<resources>
<string name="app_name">OlaAndroid</string>
<string name="ola_android">Ola Android!!!</string>
</resources>
Agora vamos adicionar uma imagem nessa tela, para isso você pode utilizar uma imagem que exista
no seu computador ou usar alguma imagem disponível na Internet, nesse exemplo estou usando um
logo do robô Android disponível em: http://developer.android.com/images/brand/Android_Robot_100.png², esse logo pode ser usado, reproduzido e modificado gratuitamente.
Salve a figura Android_Robot_100.png ou copie e cole dentro da pasta app → res → drawable.
Outro detalhe importante é que o nome das imagens pode ter apenas os caracteres [a-z0-9_],
portanto após adicionar a imagem Android_Robot_100.png aparece a seguinte mensagem de erro
no Console: res/drawable-hdpi/Android_Robot_100.png: Invalid file name: must contain only
[a-z0-9_.], porque no nome da figura tem caracteres maiúsculos.
Para resolver isso clique em cima da figura Android_Robot_100.png e escolha no menu Refactor
(Refatorar) a opção Rename (Renomear) ou clique na figura e pressione SHIFT + F6. Altere o nome
do arquivo para android.png.
No arquivo activity_main.xml após a declaração do TextView, adicione a declaração do ImageView
que fará uso da imagem do android.png:
²http://developer.android.com/images/brand/Android_Robot_100.png
Olá Android!
1
2
3
4
5
6
7
8
24
<ImageView
android:id="@+id/imagem"
android:src="@drawable/android"
android:layout_width="match_parent"
android:layout_height="250dp"
android:contentDescription="@string/ola_android"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Após adicionar a figura na pasta drawable, o Android Studio gera automaticamente um registro
para @drawable/android.
A ideia é centralizar a figura para isso utilizamos as propriedades app:layout_constraintBottom_toBottomOf="parent" e app:layout_constraintTop_toTopOf="parent" que alinha a imagem em
relação ao componente pai que nesse caso é o layout que controla toda a tela.
O Android também sugere que descrevamos a imagem que está sendo apresentado, isso pode ser feito
com a propriedade android:contentDescription= "@string/ola_android" que está utilizando a
string ola_android, esse texto será utilizado apenas para acessibilidade ele não é apresentado na
tela.
A estrutura de código e aparência visual deve ser similar a Figura 1.3.5.
25
Olá Android!
Figura 1.3.5 - Preview do projeto OlaAndroid.
Vamos adicionar uma mensagem Olá! quando a imagem do Android for clicada, para isso altere
no arquivo activity_main.xml a código XML da declaração do ImageView adicionando as últimas
duas linhas:
1
2
3
4
5
6
7
8
9
10
<ImageView
android:id="@+id/imagem"
android:src="@drawable/android"
android:layout_width="match_parent"
android:layout_height="250dp"
android:contentDescription="@string/ola_android"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clickable="true"
android:onClick="mostrarMensagem"/>
Foi adicionado a propriedade android:clickable="true" informando que essa imagem pode ser
clicada e também a propriedade android:onClick="mostrarMensagem" informando que ao clicar
na imagem será chamado o método mostrarMensagem que será declarado a seguir.
Olá Android!
26
Abra a classe MainActivity, essa é a Activity responsável pela tela inicial do aplicativo, nela importe
as classes:
1
2
import android.view.View;
import android.widget.Toast;
E adicione o método mostrarMensagem:
1
2
3
4
public void mostrarMensagem(View view) {
Toast toast = Toast.makeText(this, "Olá!", Toast.LENGTH_SHORT);
toast.show();
}
O Toast é uma mensagem que aparece e desaparece da tela e o Toast.LENGTH_SHORT especifica que
é uma mensagem que fica por um tempo curto na tela.
O código completo da classe MainActivity ficará assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.leanput.googleandroid.olaandroid;
import
import
import
import
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.view.View;
android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void mostrarMensagem(View view) {
Toast toast = Toast.makeText(this, "Olá!", Toast.LENGTH_SHORT);
toast.show();
}
}
E o código completo do activity_main.xml ficará assim:
Olá Android!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
27
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leanpub.googleandroid.olaandroid.MainActivity">
<TextView
android:id="@+id/texto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:gravity="center"
android:text="@string/ola_android"
android:textSize="40sp"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imagem"
android:src="@drawable/android"
android:layout_width="match_parent"
android:layout_height="250dp"
android:contentDescription="@string/ola_android"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clickable="true"
android:onClick="mostrarMensagem"/>
</android.support.constraint.ConstraintLayout>
Execute novamente o aplicativo no emulador do Android e clique sobre a figura para visualizar a
mensagem como mostrado na Figura 1.3.6:
28
Olá Android!
Figura 1.3.6 - Executando o projeto OlaAndroid no emulador.
1.4 Resumo
Neste capítulo foi apresentado como criar um novo projeto Android usando o Android Studio, como
criar e instalar um aplicativo no emulador. Vimos alguns componentes básicos como a Activity, e na
tela os componentes TextView e ImageView, para terminar mostramos como um método da Activity
pode ser chamado ao executar uma ação de click na figura. Esse conceito de chamada de método é
o mesmo para botões e outras ações como veremos mais adiante.
1.5 Exercícios
Exercício 1 - Crie um novo emulador para tablet, usando como base o hardware do Nexus 10 ou
superior, e execute o aplicativo OlaAndroid nele.
Exercício 2 - Altere o exemplo OlaAndroid para colocar em TexView com o seu nome logo embaixo
da figura do logo do Android.
Exercício 3 - Para alterar o título do aplicativo para Google Android devemos alterar em qual
arquivo?
Olá Android!
29
Exercício 4 - O que é o componente Toast? E o que significa definir ele com a propriedade
Toast.LENGTH_LONG?
Exercício 5 - Para definir tamanho dos componentes usamos algumas propriedades do Android
como match_parent e wrap_content, para que serve cada um deles?
2. Utilizando LinearLayout, TextView
e Button
Vamos criar um aplicativo de Calculadora para o Android. Neste exemplo será abordado a criação
de um aplicativo Android utilizando uma Activity e os componentes LinearLayout, TextView e
Button. O aplicativo ficará conforme a Figura 2.1.
Figura 2.1 - App Calculadora.
2.1 Criando o layout da tela da Calculadora
Crie um aplicativo Android igual fizemos no capítulo anterior, ou por meio do menu File → New
→ New Project, chamada Calculadora.
Vamos começar alterando o TextView que vem no layout da tela activity_main.xml, ele será
utilizado para apresentar os números da calculadora no topo da tela:
Utilizando LinearLayout, TextView e Button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
31
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leanpub.googleandroid.calculadora.MainActivity">
<TextView
android:id="@+id/resultado"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textDirection="rtl"
android:textSize="40sp"
android:textAppearance="?android:attr/textAppearanceLarge"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Nesse TextView estamos informando que ele deve manter uma margem entre o topo (android:layout_marginTop="8dp"), o componente que estiver abaixo (android:layout_marginBottom="8dp") e com
as laterais (android:layout_marginStart="8dp" e android:layout_marginEnd="8dp"); definirmos
a direção do texto da direita para a esquerda com a propriedade android:textDirection="rtl";
o tamanho do texto com a propriedade android:textSize="40sp"; a aparência grande do texto
com a propriedade android:textAppearance="?android:attr/textAppearanceLarge"; e que este
componente deve ficar alinhado no topo da tela com a propriedade app:layout_constraintTop_toTopOf="parent".
Dentro de um layout podemos adicionar outros layouts para definir como será organizado cada componente da tela.
Vamos criar logo após a declaração do <TextView ... /> e antes de fechar o </android.support.constraint
.ConstraintLayout>, um LinearLayout que permite organizar os componentes na horizontal ou
vertical, servindo para colocarmos os botões e a operações da calculadora. O LinearLayout a seguir
define os botões dos números 9, 8 e 7, e também a operação de divisão:
Utilizando LinearLayout, TextView e Button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<LinearLayout
android:id="@+id/linha1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:gravity="center_horizontal"
app:layout_constraintTop_toBottomOf="@+id/resultado">
<Button
android:id="@+id/button7"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="7"
android:textSize="40sp" />
<Button
android:id="@+id/button8"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="8"
android:textSize="40sp" />
<Button
android:id="@+id/button9"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="9"
android:textSize="40sp" />
<Button
android:id="@+id/dividir"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="/"
android:textSize="40sp" />
</LinearLayout>
32
Utilizando LinearLayout, TextView e Button
33
Este LinearLayout foi alinhado embaixo do TextView chamado resultado por meio da propriedade
app:layout_constraintTop_toBottomOf="@+id/resultado". O ID deste layout foi definido como
linha1 para servir como referência para o próximo layout.
Cada botão tem um ID diferente e suas configurações. O exemplo a seguir mostra o botão dividir, o
comprimento está sendo definido como android:layout_width="0dp", isso quer dizer que o valor
do comprimento do botão será calculado de acordo com o peso de todos os botões dentro deste
LinearLayout.
1
2
3
4
5
6
7
<Button
android:id="@+id/dividir"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="/"
android:textSize="40sp" />
A configuração do peso de cada componente é definido com a propriedade android:layout_weight="1". Como dentro deste LinearLayout há 4 botões e cada um deles está definido com o
peso "1" , isso indica que cada botão deve ter o comprimento igual a 1/4 do componente pai.
Embaixo do LinearLayout chamado linha1, vamos adicionar a segunda linha com os botões dos
números 4, 5 e 6, e também a operação multiplicação:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<LinearLayout
android:id="@+id/linha2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:gravity="center_horizontal"
app:layout_constraintTop_toBottomOf="@+id/linha1">
<Button
android:id="@+id/button4"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="4"
android:textSize="40sp" />
<Button
Utilizando LinearLayout, TextView e Button
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
android:id="@+id/button5"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="5"
android:textSize="40sp" />
<Button
android:id="@+id/button6"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="6"
android:textSize="40sp" />
<Button
android:id="@+id/multiplicar"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="*"
android:textSize="40sp" />
</LinearLayout>
Agora adicione os demais layouts e botões para deixarmos a tela como da Figura 2.1.1.
34
Utilizando LinearLayout, TextView e Button
35
Figura 2.1.1 - Layout do aplicativo da calculadora.
Para aumentar o tamanho de um botão podemos utilizar a propriedade layout_height para
aumentar a altura e a propriedade layout_width para aumentar o comprimento, se quiser utilizar o
tamanho total do componente pai, pode ser dado o valor match_parent.
Para deixar o botão 0 (zero) com o tamanho de 2 botões, deixei a propriedade android:layout_weight) do botão com valor 2, assim ele terá a largura ocupando 2/4 do espaço:
1
2
3
4
5
6
7
<Button
android:id="@+id/button0"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="2"
android:text="0"
android:textSize="40sp" />
Mais adiante veremos outras formas de redimencionar melhor os componentes na tela.
Utilizando LinearLayout, TextView e Button
36
E para deixar o botão de = (igual ou resultado) com o comprimento de quatro botões, deixei o
tamanho com valor de match_parent, assim ele obtém o tamanho total do componente pai.
1
2
3
4
5
6
<Button
android:id="@+id/igual"
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="="
android:textSize="40sp" />
2.2 Adicionando ação aos botões
Para adicionar uma ação ao clicar no botão, utilizamos a propriedade android:onClick que recebe
como valor o nome do método que será chamado na Activity, podemos alterar o botão 7 para chamar
o método adicionarNumero:
1
2
3
4
5
6
7
8
<Button
android:id="@+id/button7"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="7"
android:textSize="40sp"
android:onClick="adicionarNumero"/>
Quando clicar no botão será chamado o método adicionarNumero que deve ser declarado na classe
MainActivity e irá adicionar o valor deste botão no TextView que representa o resultado. Adicione
esse mesmo comportamento nos demais botões de números.
Na classe MainActivity adicionar esses três imports:
1
2
3
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
E o método adicionarNumero:
Utilizando LinearLayout, TextView e Button
1
2
3
4
5
37
public void adicionarNumero(View view) {
String numero = ((Button) view).getText().toString();
TextView resultado = ((TextView) findViewById(R.id.resultado));
resultado.setText(resultado.getText() + numero);
}
O método adicionarNumero recebe como parâmetro um objeto do tipo View, esse objeto serve para
representar os componentes da tela. Como sabemos que o componente da tela que chama esse
método é um botão, podemos fazer a conversão desse objeto view para um objeto do tipo Button.
Para obter o texto de um botão podemos chamar o método getText(), exemplo:
1
String numero = ((Button) view).getText().toString();
Após o botão ser pressionado o número deve aparecer escrito no TextView resultado. Para obter um
componente da tela, utilizamos o seu identificador, note que na tela declaramos o campo de texto
com a propriedade android:id="@+id/resultado" para o Android criar uma constante que aponta
para esse componente, isso é feito automaticamente gerando o arquivo R.java.
Observação: O arquivo R.java é gerado automaticamente e não devemos altera-lo. Caso
tenha algum erro nas páginas de layout, os arquivos XML, figuras, etc, essa classe R.java
não será gerada ou atualizada com os componentes da tela. É muito importante que o
layout da tela tenha sido criado corretamente antes de usar as constantes declaradas
nessa classe.
Para obter um componente da tela podemos usar o método findViewById passando como parâmetro
o id do componente que queremos obter da tela, por exemplo:
1
TextView res = ((TextView) findViewById(R.id.resultado));
Usamos o método getText() da classe Button para pegar o texto do botão, podemos usar o método
getText() do TextView para obter o seu conteúdo e também temos o método setText() para alterar
o seu conteúdo, então para mostrar o texto do Button no TextView podemos fazer assim:
1
resultado.setText(resultado.getText() + numero);
Neste ponto, se executar o aplicativo, já deverá ser possível clicar em todos os botões de número da
calculadora, como apresentado na Figura 2.2.1.
Utilizando LinearLayout, TextView e Button
38
Figura 2.2.1 - Adicionando os botões de número da calculadora.
Vamos implementar o comportamento dos botões das operações somar, subtrair, multiplicar e
dividir que deve executar o calculo da operação e o botão igual (resultado) que apresenta o resultado
na tela.
Para adicionar ação nos botões das operações de soma, subtração, multiplicação e divisão,
precisaremos de um atributo na classe MainActivity para guardar o valor que temos até o momento
no TextView resultado e também um método que será executado pela operação de soma.
Esses dois atributos podem ser declarados após a declaração da classe MainActivity:
1
2
private double total;
private String operacao;
E também adicione o método operacao, que guarda qual foi o valor do botão +, -, * ou / acionado,
guarda o valor que foi armazenado no TextView resultado e depois limpa o seu valor:
1
2
3
4
5
6
public void operacao(View view) {
operacao = ((TextView) view).getText().toString();
total = Double.valueOf(((TextView) findViewById(R.id.resultado))
.getText().toString().trim());
((TextView) findViewById(R.id.resultado)).setText("");
}
Altere os botões das operações na tela para que chamem o método operacao, exemplo:
1
2
3
4
5
6
7
8
<Button
android:id="@+id/dividir"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="/"
android:textSize="40sp"
android:onClick="operacao"/>
O método do botão igual que calcula o resultado:
Utilizando LinearLayout, TextView e Button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void calcular(View view) {
double valor = Double.valueOf(((TextView) findViewById(R.id.resultado))
.getText().toString().trim());
if("+".equals(operacao)) {
total = total + valor;
} else if("-".equals(operacao)) {
total = total - valor;
} else if("/".equals(operacao)) {
total = total / valor;
} else if("*".equals(operacao)) {
total = total * valor;
}
((TextView) findViewById(R.id.resultado)).setText(String.valueOf(total));
}
Um detalhe importante desse código é que não estamos tratando divisão por zero.
E alterar o botão igual para chamar o método calcular:
1
2
3
4
5
6
7
<Button
android:id="@+id/igual"
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="="
android:textSize="40sp"
android:onClick="calcular"/>
O método do botão CE que limpa os valores da calculadora:
1
2
3
4
5
public void limpar(View view) {
total = 0;
operacao = null;
((TextView) findViewById(R.id.resultado)).setText("");
}
E agora só falta alterar o botão CE para chamar o método limpar:
39
Utilizando LinearLayout, TextView e Button
1
2
3
4
5
6
7
8
40
<Button
android:id="@+id/limpar"
android:layout_width="0dp"
android:layout_height="80dp"
android:layout_weight="1"
android:text="CE"
android:textSize="40sp"
android:onClick="limpar"/>
Execute o aplicativo no emulador e o resultado deve ser similar ao da Figura 2.2.2.
Figura 2.2.2 - Usando o aplicativo da calculadora.
2.3 Resumo
Neste capítulo foi abordado como criar atributos e métodos na Activity, e usar os componentes
LinearLayout, TextView e Button.
Lembre-se que os métodos declarado na Activity recebem uma View como parâmetro representando
o componente da tela que está chamando o método.
Utilizando LinearLayout, TextView e Button
41
2.4 Exercícios
Exercício 1 - Altere o exemplo da calculadora e adicione um botão de ponto final (.) para representar
as casas decimais de um número.
Exercício 2 - Qual a diferença entre o LinearLayout e o ConstraintLayout?
Exercício 3 - Como podemos tratar o erro de divisão por zero na calculadora? Altere o aplicativo e
caso ocorro uma divisão por zero escreva a mensagem “ERRO” no TextView do resultado.
3. Utilizando ImageView e AlertDialog
Vamos criar um Jogo da Velha para Android, neste exemplo será abordado o uso do componente
ImageView e AlertDialog e também da criação de métodos na Activity. O aplicativo ficará conforme
a Figura 3.1:
Figura 3.1 - Jogo da Velha.
3.1 Criando layout da tela do Jogo da Velha
Crie um novo projeto Android chamado JogoDaVelha.
Observação: As imagens utilizadas neste capítulo estão disponíveis no GitHub¹.
Adicione as figuras fundo.png, o.png e x.png na pasta res → drawable, como apresentado na
Figura 3.1.1. Essas imagens representam o fundo do aplicativo, a imagem do jogador O e a imagem
do jogador X, respectivamente.
¹https://github.com/rafaelsakurai/livro-google-android
Utilizando ImageView e AlertDialog
43
Figura 3.1.1 - Estrutura de pastas do projeto.
Vamos alterar a tela activity_main.xml para adicionar a imagem fundo.png como fundo da tela e
também alterar o layout da tela para usar o LinearLayout:
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leanpub.googleandroid.jogodavelha.MainActivity"
android:background="@drawable/fundo">
</android.support.constraint.ConstraintLayout>
Note que utilizamos na propriedade android:background a imagem de fundo desse layout, veja
como ficou na Figura 3.1.2.
44
Utilizando ImageView e AlertDialog
Figura 3.1.2 - Layout de tela com fundo.
Na aba de Preview, apresentada na Figura 3.1.3, podemos configurar uma visualização semelhante a esperada no emulador ou no nosso dispositivo. Como estamos usando um emulador com
base na resolução do Nexus 5X de 1080x1920, altere o layout da versão visual para utilizar também
um tela com esse mesmo tamanho.
Figura 3.1.3 - Configuração do layout da tela.
Dentro desse layout de fundo, será adicionando mais alguns LinearLayouts para representar cada
linha do tabuleiro.
Para representar cada linha do tabuleiro do jogo da velha, será utilizado um LinearLayout, e para
representar cada área que o usuário pode clicar, será utilizar o ImageView.
Utilizando ImageView e AlertDialog
45
Vamos criar a primeira linha, nela teremos um LinearLayout para organizar os componentes e três
ImageViews que serão as imagens dos jogadores O e X:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<LinearLayout
android:id="@+id/LinearLayout01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="170dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/ImageView01"
android:layout_width="80dp"
android:layout_height="80dp"
android:clickable="true"
android:onClick="selecionar" />
<ImageView
android:id="@+id/ImageView02"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="50dp"
android:clickable="true"
android:onClick="selecionar" />
<ImageView
android:id="@+id/ImageView03"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="50dp"
android:clickable="true"
android:onClick="selecionar" />
</LinearLayout>
Observação: Não se preocupe com o erro na linha android:onClick="selecionar",
daqui a pouco criaremos o método selecionar e isso será resolvido.
Vamos fazer o mesmo com a segunda linha, mas note que agora estamos usando um espaçamento
menor em relação ao topo android:layout_marginTop="24dp", pois esse segundo LinearLayout virá
logo abaixo do primeiro LinearLayout.
Utilizando ImageView e AlertDialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/LinearLayout01">
<ImageView
android:id="@+id/ImageView04"
android:layout_width="80dp"
android:layout_height="80dp"
android:clickable="true"
android:onClick="selecionar" />
<ImageView
android:id="@+id/ImageView05"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="50dp"
android:clickable="true"
android:onClick="selecionar" />
<ImageView
android:id="@+id/ImageView06"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="50dp"
android:clickable="true"
android:onClick="selecionar" />
</LinearLayout>
E para finalizar a tela, vamos criar a última linha:
46
Utilizando ImageView e AlertDialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
47
<LinearLayout
android:id="@+id/LinearLayout03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="24dp"
android:layout_marginBottom="150dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/LinearLayout02">
<ImageView
android:id="@+id/ImageView07"
android:layout_width="80dp"
android:layout_height="80dp"
android:clickable="true"
android:onClick="selecionar" />
<ImageView
android:id="@+id/ImageView08"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="50dp"
android:clickable="true"
android:onClick="selecionar" />
<ImageView
android:id="@+id/ImageView09"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="50dp"
android:clickable="true"
android:onClick="selecionar" />
</LinearLayout>
3.2 Implementando as regras do jogo
Agora temos o layout da tela pronto, com fundo e com as imagens que serão adicionadas conforme
o andamento do jogo.
A classe MainActivity.java irá controlar as ações da tela do tabuleiro do jogo da velha. Por padrão
o Android Studio criou essa classe Activity com o seguinte código:
Utilizando ImageView e AlertDialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
48
package com.leanpub.googleandroid.jogodavelha;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
O método onCreate() é usado para criar e definir os componentes apresentados na tela. Nele é
chamado o método setContentView(R.layout.activity_main) que associa essa Activity com a
tela activity_main.xml.
No Jogo da Velha, temos algumas regras, como:
• um jogador só pode escolher uma posição que ainda está vazia;
• se o jogador montar uma sequência com três símbolos (X ou O) iguais na mesma linha, ele
vence;
• se o jogador montar uma sequência com três símbolos (X ou O) iguais na mesma coluna, ele
vence;
• se o jogador montar uma sequência com três símbolos (X ou O) iguais em uma das diagonais,
ele vence;
• se preencher todas as nove posições do tabuleiro e nenhum jogador vencer, então é considerado um empate (deu velha).
Adicione o atributo jogadorX na classe MainActivity antes do método onCreate(), esse atributo será
utilizado para controlar a vez de cada jogador.
1
private boolean jogadorX = true;
Adicione no começo da MainActivity os imports que serão utilizados:
Utilizando ImageView e AlertDialog
1
2
3
4
import
import
import
import
49
android.app.AlertDialog;
android.content.DialogInterface;
android.view.View;
android.widget.ImageView;
Vamos criar um método selecionar, que de acordo com a vez do jogador apresentará a imagem
correspondente e verificará se o jogo terminou:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void selecionar(View view) {
ImageView botao = (ImageView) view;
if(botao.getContentDescription() == null
|| "".equals(botao.getContentDescription())) {
if(jogadorX) {
botao.setImageResource(R.drawable.x);
botao.setContentDescription("X");
} else {
botao.setImageResource(R.drawable.o);
botao.setContentDescription("O");
}
jogadorX = !jogadorX;
verificarSeGanhouOuEmpatou();
}
}
Note que esse método selecione recebe como parâmetro um ImageView, pois ele será chamado cada
vez um houver um clique em algum dos componentes ImageViews da tela.
De acordo com a vez do jogador é definido a imagem e a descrição do conteúdo que será apresentada
no ImageView. Note que da mesma forma que os componentes da tela possuem IDs, as imagens
também possuem suas constantes criadas na classe R.java como R.drawable.x e R.drawable.o.
A descrição do conteúdo será usado logo a seguir para identificar os ImageViews que foram
selecionados.
Para verificar se o jogo terminou, precisamos comparar o valor de cada componente ImageView
para verificar se houve um vencedor e será necessário verificar se todos os componentes já
foram selecionados caracterizando um empate, para fazer essa verificação, vamos criar o método
verificarSeGanhouOuEmpatou():
Utilizando ImageView e AlertDialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
50
private boolean verificarSeGanhouOuEmpatou() {
String[] opcoes = montaArrayOpcoes();
String vencedor = temVencedor(opcoes);
/* Verifica se algum jogador venceu. */
if(vencedor != null && !"".equals(vencedor)) {
apresentarMensagem("O vencedor foi o: " + vencedor);
return true;
} else {
/* Se ainda não houver vencedor, verifica se deu velha. */
if(deuEmpate(opcoes)) {
apresentarMensagem("Deu Velha!");
return true;
}
}
return false;
}
Para percorrer os componentes ImageView da tela de forma mais fácil foi criado o método
montaArrayOpcoes que cria um array de String com o conteúdo da descrições dos ImageView:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private String[] montaArrayOpcoes() {
String[] opcoes = new String[9];
if(((ImageView) findViewById(R.id.ImageView01)).getContentDescription()
!= null) {
opcoes[0] = ((ImageView) findViewById(R.id.ImageView01))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView02)).getContentDescription()
!= null) {
opcoes[1] = ((ImageView) findViewById(R.id.ImageView02))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView03)).getContentDescription()
!= null) {
opcoes[2] = ((ImageView) findViewById(R.id.ImageView03))
.getContentDescription().toString();
}
Utilizando ImageView e AlertDialog
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
51
if(((ImageView) findViewById(R.id.ImageView04)).getContentDescription()
!= null) {
opcoes[3] = ((ImageView) findViewById(R.id.ImageView04))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView05)).getContentDescription()
!= null) {
opcoes[4] = ((ImageView) findViewById(R.id.ImageView05))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView06)).getContentDescription()
!= null) {
opcoes[5] = ((ImageView) findViewById(R.id.ImageView06))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView07)).getContentDescription()
!= null) {
opcoes[6] = ((ImageView) findViewById(R.id.ImageView07))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView08)).getContentDescription()
!= null) {
opcoes[7] = ((ImageView) findViewById(R.id.ImageView08))
.getContentDescription().toString();
}
if(((ImageView) findViewById(R.id.ImageView09)).getContentDescription()
!= null) {
opcoes[8] = ((ImageView) findViewById(R.id.ImageView09))
.getContentDescription().toString();
}
return opcoes;
}
Para verificar se houve vencedor, vamos criar o método temVencedor que recebe o vetor das opções
jogadas e compara cada jogada verificando se houve um vencedor:
Utilizando ImageView e AlertDialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private String temVencedor(String[] opcoes) {
String vencedor = null;
/* Verificar se a primeira linha está marcada com o mesmo texto. */
if(opcoes[0] != null && !"".equals(opcoes[0]) && opcoes[0].equals(opcoes[1])
&& opcoes[1].equals(opcoes[2])) {
vencedor = opcoes[0];
} else
/* Verificar se a segunda linha está marcada com o mesmo texto. */
if(opcoes[3] != null && !"".equals(opcoes[3]) && opcoes[3].equals(opcoes[4])
&& opcoes[3].equals(opcoes[5])) {
vencedor = opcoes[3];
} else
/* Verificar se a terceira linha está marcada com o mesmo texto. */
if(opcoes[6] != null && !"".equals(opcoes[6]) && opcoes[6].equals(opcoes[7])
&& opcoes[6].equals(opcoes[8])) {
vencedor = opcoes[6];
} else
/* Verificar se a primeira coluna está marcada com o mesmo texto. */
if(opcoes[0] != null && !"".equals(opcoes[0]) && opcoes[0].equals(opcoes[3])
&& opcoes[0].equals(opcoes[6])) {
vencedor = opcoes[0];
} else
/* Verificar se a segunda coluna está marcada com o mesmo texto. */
if(opcoes[1] != null && !"".equals(opcoes[1]) && opcoes[1].equals(opcoes[4])
&& opcoes[1].equals(opcoes[7])) {
vencedor = opcoes[1];
} else
/* Verificar se a terceica coluna está marcada com o mesmo texto. */
if(opcoes[2] != null && !"".equals(opcoes[2]) && opcoes[2].equals(opcoes[5])
&& opcoes[2].equals(opcoes[8])) {
vencedor = opcoes[2];
} else
/* Verifica se a diagonal da esquerda para a direita está marcada com o mesmo \
texto. */
if(opcoes[0] != null && !"".equals(opcoes[0]) && opcoes[0].equals(opcoes[4])
52
Utilizando ImageView e AlertDialog
43
44
45
46
47
48
49
50
51
52
53
54
55
53
&& opcoes[0].equals(opcoes[8])) {
vencedor = opcoes[0];
} else
/* Verifica se toda a diagonal da direita para a esquerda está marcada com o m\
esmo texto. */
if(opcoes[2] != null && !"".equals(opcoes[2]) && opcoes[2].equals(opcoes[4])
&& opcoes[2].equals(opcoes[6])) {
vencedor = opcoes[2];
}
return vencedor;
}
Para verificar se houve empate, vamos criar o método deuEmpate que recebe o vetor das opções e
verifica se já foram feitas todas as jogadas possíveis:
1
2
3
4
5
private boolean deuEmpate(String[] opcoes) {
return opcoes[0] != null && opcoes[1] != null && opcoes[2] != null
&& opcoes[3] != null && opcoes[4] != null && opcoes[5] != null
&& opcoes[6] != null && opcoes[7] != null && opcoes[8] != null;
}
Para apresentar uma mensagem na tela informando se houve vencedor ou deu empate, vamos
adicionar o método apresentarMensagem(), que recebe a String da mensagem que deve ser
apresentada:
1
2
3
4
5
6
7
8
9
10
11
12
13
private void apresentarMensagem(String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setMessage(msg)
.setNeutralButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
limparDados();
}
});
AlertDialog alert = builder.create();
alert.setTitle("Jogo Da Velha");
alert.show();
}
Neste exemplo usamos o componente AlertDialog para montar uma caixa de diálogo e apresentar
a mensagem se houve um vencedor ou se deu velha.
Após apresentar a mensagem, vamos chamar o método limparDados que reiniciará o jogo:
Utilizando ImageView e AlertDialog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
54
private void limparDados() {
jogadorX = true;
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
findViewById(R.id.ImageView01)).setContentDescription(null);
findViewById(R.id.ImageView02)).setContentDescription(null);
findViewById(R.id.ImageView03)).setContentDescription(null);
findViewById(R.id.ImageView04)).setContentDescription(null);
findViewById(R.id.ImageView05)).setContentDescription(null);
findViewById(R.id.ImageView06)).setContentDescription(null);
findViewById(R.id.ImageView07)).setContentDescription(null);
findViewById(R.id.ImageView08)).setContentDescription(null);
findViewById(R.id.ImageView09)).setContentDescription(null);
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
((ImageView)
findViewById(R.id.ImageView01)).setImageResource(0);
findViewById(R.id.ImageView02)).setImageResource(0);
findViewById(R.id.ImageView03)).setImageResource(0);
findViewById(R.id.ImageView04)).setImageResource(0);
findViewById(R.id.ImageView05)).setImageResource(0);
findViewById(R.id.ImageView06)).setImageResource(0);
findViewById(R.id.ImageView07)).setImageResource(0);
findViewById(R.id.ImageView08)).setImageResource(0);
findViewById(R.id.ImageView09)).setImageResource(0);
}
Execute o aplicativo no emulador do Android para verificar como ficou, deve ficar semelhante a
Figura 3.2.1 e aproveite para jogar um pouco e descontrair.
55
Utilizando ImageView e AlertDialog
Figura 3.2.1 - Jogo da Velha.
3.3 Resumo
Neste capítulo usamos basicamente os mesmos componentes já apresentado, mas com algumas
variações, na tela adicionamos componente ImageView que são carregados dinamicamente e também
criamos uma forma mais comum de caixa de diálogo chamado AlertDialog.
Também declaramos mais métodos na Activity para tentar deixar o código mais organizado. Mais
adiante apresentaremos como organizar melhor os componentes da tela para que eles fiquem
visivelmente alinhados em diversas resoluções diferentes.
3.4 Exercícios
Exercício 1 - Esse jogo da velha foi criado para ser jogado com duas pessoas, altere o jogo para jogar
contra o computador.
4. Requisitando outra tela com Intent
Vamos criar um aplicativo Android chamado MinhasTarefas para registro de tarefas. Nesse projeto
será abordado o uso de Intent para chamar uma Activity, teremos uma tela de autenticação, na
qual usuário informa seu login e senha e uma tela para adicionar tarefas. Também utilizaremos os
componentes EditText e ListView.
Figura 4.1 - Minhas tarefas.
Observação: A imagem utilizada neste capítulo estão disponíveis no GitHub¹.
4.1 Criando uma tela de autenticação
Para começar, crie a tela que será utilizada para autenticação, como apresentada na Figura 4.1.1.
Nessa tela haverá uma figura do logo, os rótulos de Login: e Senha:, os campos para digitação do
¹https://github.com/rafaelsakurai/livro-google-android
57
Requisitando outra tela com Intent
login e senha e o botão para entrar na tela de tarefas.
Figura 4.1.1 - Tela de autenticação.
Monte na activity_main.xml a estrutura da tela, adicione:
1- Uma ImageView com o logo.png na parte superior da tela;
2- Um TextView com o rótulo Login:, lembre de guardar todas as Strings no arquivo res/values/strings.xml.
3- Um EditText (caixa de texto) para digitar a informação do login.
4- Um TextView com o rótulo Senha:, lembre de guardar todas as Strings no arquivo res/values/strings.xml.
5- Um EditText para digitar a informação da senha. Para representar um campo de senha utilize a
propriedade android:inputType="textPassword" do componente EditText, exemplo:
Requisitando outra tela com Intent
1
2
3
4
5
6
58
<EditText
android:id="@+id/senha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:inputType="textPassword"/>
6- Botão para Entrar, para representar o botão utilize o componente Button, exemplo:
1
2
3
4
5
6
7
8
9
<Button
android:id="@+id/entrar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:onClick="entrar"
android:text="@string/entrar"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent" />
Observação: durante a criação da tela informe o atributo android:id dos componentes
que serão usados pelas classes Activity, pois é através do id que obtemos esses atributos.
Nesse momento temos o layout da primeira tela criado, mas ainda não é funcional.
No código usado nessa tela, defini que a figura deve ficar alinhada no topo da tela, e os demais
componentes estão alinhados com base no fundo da tela. Ficando assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leanpub.googleandroid.minhastarefas.MainActivity"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp">
<ImageView
android:id="@+id/logo"
Requisitando outra tela com Intent
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/logo"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/linear1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
app:layout_constraintBottom_toTopOf="@+id/linear2">
<TextView
android:id="@+id/textLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login"
android:textSize="20sp"/>
<EditText
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
app:layout_constraintTop_toBottomOf="@+id/logo"
tools:layout_editor_absoluteX="81dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/entrar">
<TextView
android:id="@+id/textSenha"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/senha"
59
Requisitando outra tela com Intent
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
60
android:textSize="20sp" />
<EditText
android:id="@+id/senha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:inputType="textPassword"/>
</LinearLayout>
<Button
android:id="@+id/entrar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:onClick="entrar"
android:text="@string/entrar"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
Observação: Ficará um erro no Button porque ainda não está implementado o método
entrar, mas daqui a pouco isso será resolvido.
4.2 Criando a tela da lista de tarefas
Vamos criar uma nova tela para representar a listagem das tarefas, para criar um novo layout, clique
com o botão direito do mouse na pasta app → res → layout e escolha New → Layout resource
file.
Na tela de New Resource File defina:
• File name (Nome do arquivo): tarefa
• Root element (Elemento raiz): ConstraintLayout
Clique em OK para criar o arquivo de layout. A Figura 4.2.1 apresenta um exemplo de criação de
layout de tela.
61
Requisitando outra tela com Intent
Figura 4.2.1 - Novo layout de tela.
O arquivo de layout criado representa uma tela do aplicativo Android, vamos colocar nessa tela um
campo para digitar o texto da tarefa, um botão Adicionar e uma lista de elementos, como na Figura
4.2.2.
Figura 4.2.2 - Tela de tarefas.
Para adicionar uma lista na tela vamos utilizar o componente ListView, abra o arquivo tarefa.xml
Requisitando outra tela com Intent
e adicione o seguinte exemplo:
1
2
3
4
5
<ListView
android:id="@+id/listaTarefas"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
A seguir é apresentado o código para a tela tarefa.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.andro\
id.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp">
<EditText
android:id="@+id/tarefa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/adicionar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/adicionar"
android:onClick="adicionarTarefa"
app:layout_constraintTop_toBottomOf="@+id/tarefa" />
<ListView
android:id="@+id/listaTarefas"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/adicionar"/>
</android.support.constraint.ConstraintLayout>
62
63
Requisitando outra tela com Intent
A execução das ações desta tela de tarefas ficará em uma nova Activity. Vamos criar uma nova
classe Activity, para isso clique com o botão direito do mouse no mesmo diretório em que está a
MainActivity, escolha New (Novo) → Java Class, informe o Name (Nome) como TarefaActivity,
em Superclass informe AppCompatActivity, e o nome do Package (Pacote), aqui provavelmente
já deve aparecer o mesmo nome de package da MainActivity, caso contrário escreva o nome do
package. A Figura 4.2.3 apresenta um exemplo de criação de uma Activity.
Figura 4.2.3 - Criando uma nova Activity.
Altere o código da classe TarefaActivity.java para ficar com o seguinte conteúdo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.leanpub.googleandroid.minhastarefas;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class TarefaActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tarefa);
}
}
A chamada do método setContentView(R.layout.tarefa) informa que esta Activity usa o layout de
tela tarefa.xml.
Requisitando outra tela com Intent
64
Para o aplicativo reconhecer que existe esta nova Activity, precisamos informar no arquivo
AndroidManifest.xml, inclua <activity android:name=".TarefaActivity"/> após a declaração
da tag activity MainActivity e dentro da tag application
A declaração completa do AndroidManifest.xml fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.leanpub.googleandroid.minhastarefas" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".TarefaActivity"/>
</application>
</manifest>
Um detalhe importante, quando colocamos o símbolo de ponto antes do nome da activity como
".TarefaActivity", é porque estamos dizendo que está activity está dentro do pacote mencionado
na propriedade package="com.leanpub.googleandroid.minhastarefas" da tag manifest. Se a
Activity for criada em outro pacote, é necessário mencionar o caminho completo como: <activity
android:name="googleandroid.exemplo.TarefaActivity"/>.
4.3 Usando o Intent
Agora que as duas telas estão prontas, podemos adicionar o comportamento nas telas. Na tela inicial
temos o botão entrar que fará uma autenticação, verificando se o login e senha digitados são corretos.
Na MainActivity adicione os seguintes imports:
Requisitando outra tela com Intent
1
2
3
4
import
import
import
import
65
android.content.Intent;
android.view.View;
android.widget.EditText;
android.widget.Toast;
Ao clicar no botão Entrar da activity_main.xml, chame o método entrar(View view) na MainActivity.java.
1
2
3
4
5
6
7
8
9
public void entrar(View view) {
EditText login = (EditText) findViewById(R.id.login);
EditText senha = (EditText) findViewById(R.id.senha);
if ("admin".equals(login.getText().toString()) &&
"123".equals(senha.getText().toString())) {
//chamar a tela de tarefas.
}
}
O método entrar(View view) acessa os EditText de login e senha da tela e compara com os valores
“admin” e “123” respectivamente, mais adiante será apresentado como guardar essas informações
em um banco de dados para não deixar fixo no código.
Observação: Note que no método findViewById precisamos passar o ID do componente
XML, então se você não definiu nenhum ID no momento da criação do layout, altere o
XML do layout adicionando os IDs de cada componente.
Para chamar uma Activity utilizaremos a classe android.content.Intent, sempre que houver a
intenção de chamar um componente do aplicativo, iniciar algum aplicativo do Android, reproduzir
uma musica, entre outros, utilizamos o Intent. Para chamar uma tela utilizando a Intent, usamos
o método startActivity() que a classe recebe via herança da classe Activity e passamos para esse
método a Intent, exemplo:
1
startActivity(new Intent(this, TarefaActivity.class));
Então se o usuário informar os dados corretos de login e senha, será aberto a tela das tarefas. Mas
se o usuário digitar alguma informação incorreta, podemos enviar uma mensagem informando que
o login ou senha está incorreto, exemplo:
66
Requisitando outra tela com Intent
1
2
3
else {
Toast.makeText(this, "Login ou senha inválida!", Toast.LENGTH_SHORT).show();
}
Caso o usuário digite alguma informação incorreta, aparecerá uma mensagem Toast de aviso na
tela.
Figura 4.3.1 - Erro na autenticação.
O código completo da MainActivity ficou assim:
1
2
3
4
5
6
7
8
9
10
11
package com.leanpub.googleandroid.minhastarefas;
import
import
import
import
import
import
android.content.Intent;
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.view.View;
android.widget.EditText;
android.widget.Toast;
public class MainActivity extends AppCompatActivity {
Requisitando outra tela com Intent
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
67
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void entrar(View view) {
EditText login = (EditText) findViewById(R.id.login);
EditText senha = (EditText) findViewById(R.id.senha);
if ("admin".equals(login.getText().toString()) &&
"123".equals(senha.getText().toString())) {
startActivity(new Intent(this, TarefaActivity.class));
} else {
Toast.makeText(this, "Login ou senha inválida!",
Toast.LENGTH_SHORT).show();
}
}
}
Com a tela de autenticação funcionando, vamos adicionar o comportamento na tela da lista de
tarefas.
Na classe TarefaActivity adicione os seguintes imports:
1
2
3
4
5
6
7
8
import
import
import
import
import
android.view.View;
android.widget.ArrayAdapter;
android.widget.EditText;
android.widget.ListView;
android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
Também adicione uma lista de String, que serão as tarefas que devem ser apresentadas na lista e
para adicionar elementos no ListView, vamos utilizar um ArrayAdapter, exemplo:
1
2
private List<String> tarefas;
private ArrayAdapter<String> adapter;
Altere o método onCreate para criar uma lista das tarefas:
Requisitando outra tela com Intent
1
2
3
4
5
6
7
8
9
10
11
68
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tarefa);
tarefas = new ArrayList<String>();
ListView lista = (ListView) findViewById(R.id.listaTarefas);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, tarefas);
lista.setAdapter(adapter);
}
No momento de instanciar um ArrayAdapter é informado a Activity atual com o atributo
this. Para o layout da ListView, vamos utilizar um padrão já disponível no Android chamado
android.R.layout.simple_list_item_1, mas posteriormente veremos como criar um layout personalizado para a ListView. E por último informamos a lista de Strings que contém aos itens que serão
apresentados na lista.
Quando o usuário clicar no botão Adicionar da tela de tarefas, faça uma chamada para o método
adicionarTarefa(View view) que obtém o valor do campo descrição e se ele estiver preenchido,
adiciona o na lista, caso contrário apresenta uma mensagem informativa.
1
2
3
4
5
6
7
8
9
10
public void adicionarTarefa(View view) {
EditText descricao = (EditText) findViewById(R.id.tarefa);
if(descricao.getText().toString().trim().length() > 0) {
tarefas.add(descricao.getText().toString());
adapter.notifyDataSetChanged();
descricao.setText("");
} else {
Toast.makeText(this, "Campo tarefa vazio!", Toast.LENGTH_SHORT).show();
}
}
Associe o método adicionarTarefa com o botão Adicionar que temos no layout da tarefa.xml.
Execute o aplicativo e cadastre algumas tarefas.
Requisitando outra tela com Intent
69
Figura 4.3.2 - Usando o aplicativo Minhas Tarefas.
4.4 Resumo
Neste capítulo usamos o Intent para solicitar abrir um Activity e usamos o ListView para guardar
as tarefas que foram criadas.
No próximo capítulo apresentaremos como enviar informações na solicitação ou retorno de uma
Intent usando o Bundle.
4.5 Exercícios
Exercício 1 - Crie um aplicativo que na primeira tela possua a imagem de algum jogo de tabuleiro
e um botão chamado Regras do Jogo, quando o usuário tocar no botão deve apresentar uma nova
activity que apresenta as regras do jogo escolhido.
5. Trocando dados entre telas via
Bundle
No capítulo anterior foi abordado como uma Activity pode chamar outra Activity usando o Intent,
mas dependendo da situação precisamos passar alguns dados nessa mesma chamada.
5.1 Bundle
Para isto, podemos utilizar o Bundle que consegue guardar diversas informações e pode ser enviado
no Intent. Exemplo:
1
2
3
4
5
6
7
Bundle bundle = new Bundle();
bundle.putString("string", valor);
bundle.putInt("int", numero);
Intent intent = new Intent(getBaseContext(), MinhaActivity.class);
intent.putExtras(bundle);
startActivity(intent);
Nesse exemplo, um Bundle é criado e nele pode ser adicionado diversos tipos de dados por meio dos
métodos putXxx(), como: putString(String chave, String valor) que recebe como parâmetro
uma String de chave e outra String com o seu valor. Quando cria a Intent e passa o bundle para o
método putExtras, essas informações estão sendo enviadas para a Activity que será chamada.
Na activity chamada pelo Intent é possível pegar os dados do bundle, assim:
1
2
3
Bundle bundle = getIntent().getExtras();
String texto = bundle.getString("string");
int numero = bundle.getInt("int");
Desse modo, é possível por exemplo, obter os dados no bundle da Intent no método onCreate e
usar para inicializar algum valor. Para isso use o método getIntent().getExtras() que devolve o
Bundle enviado pela Intent e por meio dele é possível obter as informações com métodos getXxx(),
como getString("string") que recebe como parâmetro a chave do valor adicionado no bundle.
Observação: Veremos mais adiante que o Intent pode ser usado para diversas outras
coisas além de chamar uma Activity.
Trocando dados entre telas via Bundle
71
Um Bundle também pode ser usado para devolver uma resposta para chamada de uma Activity. Até
os capítulos anteriores foi utilizado o método startActivity(Intent intent) para chamar uma
Activity, mas também há o método startActivityForResult(Intent intent, int requestCode)
e um outro método sobrecarregado que também recebe um Bundle como parâmetro.
O método startActivityForResult é usado para obter uma resposta de quem foi chamado pela
Intent e para isso é necessário implementar na Activity iniciou o Intent o método onActivityResult
que tratará o retorno da Intent. Exemplo:
1
2
3
4
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
5.2 Criando o projeto de Valet
Neste capítulo, crie um novo projeto chamado Valet, mas no momento de escolher qual Activity
utilizar no projeto, escolha a opção Basic Activity, como mostrado na Figura 5.2.1. Nesse exemplo
vamos utilizar este botão flutuante que tem na tela inicial para realizar a troca de telas.
Trocando dados entre telas via Bundle
72
Figura 5.2.1 - Adicionando uma activity basica ao projeto.
Após escolher a opção Basic Activity clique em Next para continuar. Na tela Configure Activity
(apresentada na Figura 5.2.2) aparece algumas opções a mais, mas não iremos alterar, por tanto
clique em Finish para terminar a criação do projeto.
73
Trocando dados entre telas via Bundle
Figura 5.2.2 - Configurando a activity.
Note na Figura 5.2.3 que agora o conteúdo da tela inicial está dentro do arquivo content_main.xml, mas ainda temos também o layout da activity_main.xml que iremos alterar mais adiante.
Trocando dados entre telas via Bundle
74
Figura 5.2.3 - Estrutura do projeto Valet.
Este projeto terá duas telas para controle de estacionamento. A primeira tela tem uma lista de
mostrando o modelo, placa e data de entrada dos veículos no Valet. Esta tela também tem o menu
flutuante com o símbolo de +, que redireciona para a tela que cadastra um novo veículo no Valet. A
Figura 5.2.4 mostra como ficará esse aplicativo.
75
Trocando dados entre telas via Bundle
Figura 5.2.4 - App Valet.
A tela inicial content_main.xml servirá para mostrar uma lista de veículos com um TextView e um
ListView, altere para que ela fique com a aparência apresentada na Figura 5.2.5.
76
Trocando dados entre telas via Bundle
Figura 5.2.5 - Estrutura da tela inicial.
O código fonte da content_main.xml ficou assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.leanpub.googleandroid.valet.MainActivity"
tools:showIn="@layout/activity_main">
<TextView
android:id="@+id/titulo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="@string/app_name"
android:textSize="30sp"
app:layout_constraintTop_toTopOf="parent" />
Trocando dados entre telas via Bundle
21
22
23
24
25
26
27
28
29
77
<ListView
android:id="@+id/lista"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/titulo" />
</android.support.constraint.ConstraintLayout>
Agora vamos alterar o botão flutuante que está com um símbolo de carta de correio para um símbolo
de +, como mostrado na Figura 5.2.6.
Figura 5.2.6 - Alterando o botão flutuante.
Adicione o ícone ic_add_48px.xml na pasta app → res → drawable do projeto. Este ícone está
disponível no GitHub¹.
No activity_main.xml procure pela tag FloatingActionButton:
1
2
3
4
5
6
7
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
Altere a propriedade app:srcCompat="@android:drawable/ic_dialog_email" para android:src="@drawable/ic_add_48px". O código completo do FloatingActionButton ficou assim:
¹https://github.com/rafaelsakurai/livro-google-android
Trocando dados entre telas via Bundle
1
2
3
4
5
6
7
78
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_add_48px" />
Agora vamos trocar a cor de fundo do botão flutuante para um tom de azul, assim ficando similar a
barra superior do aplicativo.
Abra o arquivo colors.xml que está na pasta app → res → values. E altere a cor colorAccent
para:
1
<color name="colorAccent">#92A8D1</color>
5.3 Adicionando a tela de cadastro de veículos
Na pasta app → res → layout crie um arquivo layout chamado valet.xml possuindo o formulário
para preencher o modelo e placa do veículo, similar a Figura 5.3.1.
Figura 5.3.1 - Alterando o botão flutuante.
O código completo dessa tela ficou assim:
Trocando dados entre telas via Bundle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<TextView
android:id="@+id/titulo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/novo_carro"
android:textSize="30sp"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/layout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/titulo">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/modelo"/>
<EditText
android:id="@+id/modelo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/layout2"
79
Trocando dados entre telas via Bundle
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
80
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/layout1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/placa"/>
<EditText
android:id="@+id/placa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapCharacters"
android:layout_marginLeft="8dp"/>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/salvar"
android:layout_gravity="center"
android:textSize="20sp"
android:onClick="adicionar"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/layout2"/>
</android.support.constraint.ConstraintLayout>
O componente EditText pode ser personalizado para ajudar a digitação do usuário. Uma pequena
modificação foi feita no segundo EditText que serve para digitar a placa, nele foi adicionado
a propriedade android:inputType="textCapCharacters" para que todos os caracteres digitados
fiquem em maiúsculo.
No arquivo strings.xml adicione as strings de modelo, placa e salvar:
1
2
3
<string name="modelo">Modelo</string>
<string name="placa">Placa</string>
<string name="salvar">Salvar</string>
Então, já está pronto layout das duas telas, agora vamos montar o comportamento destas telas.
Trocando dados entre telas via Bundle
81
5.4 Chamando a tela de cadastro de veículo
Dentro do pacote java do projeto, crie uma nova classe chamada ValetActivity que herda de
AppCompatActivity e implementa o método onCreate, exemplo:
1
2
3
4
5
6
7
8
9
10
11
12
package com.leanpub.googleandroid.valet;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class ValetActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.valet);
}
}
Esta activity é responsável pelo layout da tela valet.xml. Lembrando que para o aplicativo
reconhecer que existe esta nova ValeActivity.class, é necessário informar no arquivo AndroidManifest.xml, após a declaração da tag activity MainActivity e dentro da tag application, a
declaração completa fica assim:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.leanpub.googleandroid.valet">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Trocando dados entre telas via Bundle
19
20
21
22
23
24
82
</intent-filter>
</activity>
<activity android:name=".ValetActivity"/>
</application>
</manifest>
O botão flutuante da tela principal será utilizado para chamar a tela de cadastro do veículo. Na
classe MainActivity procure no método onCreate a implementação do FloatingActionButton, por
padrão essa implementação apresenta uma mensagem de aviso, altere o conteúdo para chamar outra
TarefaActivity:
1
2
3
4
5
6
7
8
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getBaseContext(), ValetActivity.class);
startActivityForResult(intent, 1);
}
});
Agora ao clicar no botão flutuante será criado um Intent para a ValetActivity, só que para iniciar
esta activity foi usado o método startActivityForResult, pois a ValetActivity devolverá os dados
do veículo que serão adicionados na lista de valet.
Na ValetActivity adicione os imports:
1
2
3
4
import
import
import
import
android.app.Activity;
android.content.Intent;
android.view.View;
android.widget.EditText;
Também inclua na ValetActivity o método adicionar:
Trocando dados entre telas via Bundle
1
2
3
4
5
6
7
8
9
83
public void adicionar(View view) {
Intent intent = new Intent();
intent.putExtra("modelo", ((EditText) findViewById(R.id.modelo))
.getText().toString());
intent.putExtra("placa", ((EditText) findViewById(R.id.placa))
.getText().toString());
setResult(Activity.RESULT_OK, intent);
finish();
}
O método adicionar será executado quando o usuário clicar (tocar) no botão SALVAR do formulário. Um Intent é criado com os dados preenchidos de modelo e placa; a chamada do método
setResult(Activity.RESULT_OK, intent) indica que a chamada desta tela foi feita com sucesso e
que está retornando os dados para a tela inicial; também há uma chamada para o método finish()
que finaliza está activity.
Até este ponto, se você executar o aplicativo, verá que o fluxo está funcionando, da tela inicial, vai
para a tela de cadastro do veículo e volta para a tela inicial, mas nenhum dado está sendo inserido
na lista de valet.
5.5 Obtendo o retorno da chamada da Activity
Na MainActivity adicione os seguintes imports:
1
2
3
4
5
6
7
8
import android.app.Activity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import
import
import
import
java.text.DateFormat;
java.util.ArrayList;
java.util.Date;
java.util.List;
Para implementar o retorno da chamada da tela de cadastro de veículo, crie a lista de carros e o
formatador de data/hora na MainActivity:
Trocando dados entre telas via Bundle
1
2
3
4
5
84
private List<String> carros = new ArrayList();
private ArrayAdapter<String> adapter;
private DateFormat df = null;
private DateFormat hf = null;
Para inicializar a ListView e os formatadores de data, altere o método onCreate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getBaseContext(), ValetActivity.class);
startActivityForResult(intent, 1);
}
});
df = android.text.format.DateFormat.getDateFormat(getApplicationContext());
hf = android.text.format.DateFormat.getTimeFormat(getApplicationContext());
ListView lista = (ListView) findViewById(R.id.lista);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, carros);
lista.setAdapter(adapter);
}
Foi inicializado um formatador para data e outro para hora, esses formatadores estão obtendo o
formato padrão de acordo as preferências de local da instalação do Android. Também está criando
um ArrayAdapter que permitirá adicionar Strings na lista.
Agora implemente o método onActivityResult que fará o tratamento do retorno da tela de cadastro
de veículo.
Trocando dados entre telas via Bundle
1
2
3
4
5
6
7
8
9
10
11
12
85
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Activity.RESULT_OK == resultCode) {
String modelo = data.getStringExtra("modelo");
String placa = data.getStringExtra("placa");
Date agora = new Date();
carros.add(modelo + " - " + placa + "\nEntrada: " + df.format(agora)
+ " " + hf.format(agora));
adapter.notifyDataSetChanged();
}
}
O método onActivityResult verifica se o resultCode é igual a Activity.RESULT_OK, se for, então
pega as informações de modelo e placa passados pelo bundle do Intent e cria uma nova data, com
esses dados adiciona uma nova string com o cadastro do veículo na lista.
Pronto, agora teste o aplicativo e verá que agora está adicionando os veículos na lista, como mostrado
na Figura 5.4.1.
Figura 5.4.1 - Cadastro de vários veículos.
Para melhorar esse exemplo, vamos permitir remover os veículos cadastrados.
Trocando dados entre telas via Bundle
86
5.6 Removendo os elementos da lista
Para fazer isso vamos criar uma ação de click na lista, que quando for acionado abre uma caixa de
dialogo perguntando se deseja retirar o veículo.
Na classe MainActivity adicione os imports:
1
2
3
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.widget.AdapterView;
Também adicione os atributos para representar a Caixa de Dialogo e o carro selecionado:
1
2
private AlertDialog dialog;
private String carroSaida = null;
No final do método onCreate adicione o listener de click da lista:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getBaseContext(), ValetActivity.class);
startActivityForResult(intent, 1);
}
});
df = android.text.format.DateFormat
.getDateFormat(getApplicationContext());
hf = android.text.format.DateFormat
.getTimeFormat(getApplicationContext());
ListView lista = (ListView) findViewById(R.id.lista);
adapter = new ArrayAdapter<String>(this,
Trocando dados entre telas via Bundle
24
25
26
27
28
29
30
31
32
33
34
35
36
87
android.R.layout.simple_list_item_1, carros);
lista.setAdapter(adapter);
lista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
carroSaida = carros.get(position);
dialog = confirmarSaida();
dialog.show();
}
});
}
O método setOnItemClickListener implementa um AdapterView.OnItemClickListener que controla o clique na lista. Quando algum carro da lista da lista for clicado, será montado uma caixa de
dialogo através do método confirmarSaida:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public AlertDialog confirmarSaida() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.saida);
builder.setMessage(carroSaida);
builder.setPositiveButton(getString(R.string.sim),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
carros.remove(carroSaida);
adapter.notifyDataSetChanged();
}
});
builder.setNegativeButton(getString(R.string.nao), null);
return builder.create();
}
Este método cria um AlertDialog, informando um título, uma mensagem e a ação do botão Sim
representado pelo positiveButton da caixa de dialogo. Se clicar no botão Sim o carro selecionado
será removido da lista.
No arquivo strings.xml adicione as strings de saida, sim e nao:
88
Trocando dados entre telas via Bundle
1
2
3
<string name="saida">Saída do veículo</string>
<string name="sim">Sim</string>
<string name="nao">Não</string>
Execute novamente o aplicativo e teste a saída de um veículo, como mostrado na Figura 5.5.1.
Figura 5.5.1 - Saída do veículo.
Você já deve ter percebido que os dados do veículo ficam salvos apenas durante a execução do
aplicativo, cada vez que você publicar o aplicativo novamente no emulador os dados são apagados.
No próximo capítulo veremos como persistir as informações usando o SQLite.
5.7 Resumo
Neste capítulo foi apresentado o uso do Bundle como forma de passar informações na chamada ou
retorno de um Intent, vimos como podemos facilmente adicionar ou remover uma informação em
uma ListView e também utilizamos o AlertDialog para montar uma caixa de dialogo.
No próximo capítulo veremos como persistir dados usando o banco de dados interno do Android
chamado SQLite.
Trocando dados entre telas via Bundle
89
5.8 Exercícios
Exercício 1 - Altere o projeto de Tarefas apresentado no Capítulo 3 implementando a opção de
remover uma tarefa.
Exercício 2 - Como podemos alterar o projeto do Valet para que ele calcule e apresente no momento
da saída do veículo o valor que deve ser pago pelo período de estacionamento do veículo?
6. Persistindo dados com SQLite
Neste capítulo, vamos alterar o aplicativo Android de Valet criado no capítulo 5, para persistir os
veículos cadastrados no SQLite.
Figura 6.1 - Lista de veículos salvos com SQLite.
Crie uma classe Java chamada Valet no pacote do projeto, esta classe será usada para representar
cada veículo no estacionamento:
1
2
3
4
5
6
7
8
9
package com.leanpub.googleandroid.valet;
import java.util.Date;
public class Valet {
private Long _id;
private String modelo;
private String placa;
private Date entrada;
Persistindo dados com SQLite
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private Date saida;
private double preco;
public Long getId() {
return _id;
}
public void setId(Long _id) {
this._id = _id;
}
public String getModelo() {
return modelo;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public String getPlaca() {
return placa;
}
public void setPlaca(String placa) {
this.placa = placa;
}
public Date getEntrada() {
return entrada;
}
public void setEntrada(Date entrada) {
this.entrada = entrada;
}
public Date getSaida() {
return saida;
}
public void setSaida(Date saida) {
this.saida = saida;
}
91
Persistindo dados com SQLite
52
53
54
55
56
57
58
59
60
92
public double getPreco() {
return preco;
}
public void setPreco(double preco) {
this.preco = preco;
}
}
6.1 Montando o banco de dados no SQLite
O Android possui internamente um banco de dados SQLite, no qual é possível criarmos tabelas, criar
relacionamentos, executar operações de inclusão, alteração, exclusão, consultas, entre outros.
Para começar, crie uma classe Java chamada ValetDB no pacote do projeto, que será utilizada para
criar a tabela no SQLite. Faça essa classe herdar de android.database.sqlite.SQLiteOpenHelper
e implementar os métodos onCreate e onUpgrade:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.leanpub.googleandroid.valet;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class ValetDB extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
O método onCreate é chamado quando o aplicativo é instalado no celular e o método onUpgrade
é chamado quando já tem uma versão do aplicativo instalado no celular e há novas estruturas do
banco de dado para serem atualizadas.
Dentro da classe ValetDB crie um construtor que receberá como parâmetro um android.context.Context
e iniciará a base de dados chamada Valet e sua versão 1:
Persistindo dados com SQLite
1
2
3
93
public ValetDB(Context context) {
super(context, "Valet", null, 1);
}
Altere a implementação do método onCreate para criar a tabela que armazenará os veículos:
1
2
3
4
5
6
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE Valet (_id INTEGER PRIMARY KEY " +
" AUTOINCREMENT, modelo TEXT, placa TEXT, entrada TEXT, " +
" saida TEXT, valor REAL);");
}
Este método será usado para criar todas as tabelas do banco de dados logo que o aplicativo for
iniciado pela primeira vez e também pode ser usado para adicionar algumas informações iniciais no
banco de dados.
Na classe ValetDB vamos implementar os métodos para salvar ou alterar os dados de um veículo, e
também consultar todos veículos que ainda estão no estacionamento.
Comece adicionando os imports:
1
2
3
4
5
6
7
import android.content.ContentValues;
import android.database.Cursor;
import
import
import
import
java.text.DateFormat;
java.text.SimpleDateFormat;
java.util.ArrayList;
java.util.List;
Adicione o método salvarValet para salvar ou atualizar os dados do veículo no banco de dados:
1
2
3
4
5
6
7
8
9
10
public Valet salvarValet(Valet valet) {
SQLiteDatabase db = getWritableDatabase();
try {
ContentValues values = new ContentValues();
values.put("modelo", valet.getModelo());
values.put("placa", valet.getPlaca());
values.put("entrada", df.format(valet.getEntrada()));
if (valet.getId() == null) {
long id = db.insert("Valet", null, values);
Persistindo dados com SQLite
11
12
13
14
15
16
17
18
19
20
21
22
23
94
valet.setId(id);
} else {
values.put("saida", df.format(valet.getSaida()));
values.put("valor", valet.getPreco());
String[] where = new String[]{String.valueOf(valet.getId())};
db.update("Valet", values, "_id = ?", where);
}
} finally {
db.close();
}
return valet;
}
A classe ValetDB que estende a classe SQLiteOpenHelper recebe via herança o método getWritableDatabase() que fornece um objeto SQLiteDatabase com acesso de escrita na base de dados.
Este método verifica se o objeto valet não tiver um id significa que ele é novo e para inserir os
registros no banco de dados SQLite, o SQLiteDatabase tem o método insert que recebe como
parâmetro o nome da tabela e um objeto ContentValues com os valores que serão inseridos.
Se houver id no objeto valet, então atualiza o registro e para isso utiliza o método update do
SQLiteDatabase. Este método recebe como parâmetro o nome da tabela, valores do registro que
será alterado, critério de atualização e o parâmetro do critério de atualização.
Um detalhe importante é que para salvar as datas no SQLite está sendo usado uma
String no padrão yyyy-MM-dd HH:mm:ss, porque o ContentValue e o Cursor não
tem métodos para trabalhar com tipos Date.
Para formatar a data no padrão declare também um atributo de DateFormat:
1
private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Para consultar todos os veículos no valet que estão salvos no banco de dados, vamos criar o método
consultarVeiculosValet():
Persistindo dados com SQLite
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
95
public List<Valet> consultarVeiculosValet() {
List<Valet> lista = new ArrayList();
SQLiteDatabase db = getReadableDatabase();
try {
Cursor cursor = db.rawQuery("SELECT _id, modelo, placa, entrada " +
" FROM Valet where saida is null", null);
cursor.moveToFirst();
for (int i = 0; i < cursor.getCount(); i++) {
Valet valet = new Valet();
valet.setId(cursor.getLong(0));
valet.setModelo(cursor.getString(1));
valet.setPlaca(cursor.getString(2));
valet.setEntrada(df.parse(cursor.getString(3)));
lista.add(valet);
cursor.moveToNext();
}
cursor.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
db.close();
}
return lista;
}
Para executar uma consulta no SQLite foi utilizado o método getReadableDatabase() da classe
SQLiteOpenHelper que devolve um SQLiteDatabase com permissão de leitura na base de dados.
Com o método rawQuery() é possível executar uma consulta e obter um Cursor que possui o
resultado da consulta. Para percorrer os resultados do cursor podemos utilizar os métodos:
• moveToFirst() que move o cursor para a primeira linha, se o cursor estiver vazio retorna false;
• moveToLast() que move o cursor para a última linha, se o cursor estiver vazio retorna false;
• moveToNext() que move o cursor para a próxima linha, se o cursor estiver na última linha
retorna false;
• moveToPosition(int position) que move o cursor para uma linha informada, retorna false se
não encontrar a linha informada;
• moveToPrevious() que move o cursor para a linha anterior, se o cursor estiver na primeira
linha retorna false.
Após terminar de utilizar o cursor lembre-se de chamar o método close().
Persistindo dados com SQLite
96
6.2 Adicionando a persistência do veículos
Agora vamos alterar a classe MainActivity para salvar os dados do veículo, para isso inclua o
atributo do ValetDB nela:
1
2
private ValetDB db;
private List<Valet> valets = new ArrayList();
Também aproveite para alterar o tipo do atributo carroSaida de String para representar o objeto
Valet selecionado.
1
private Valet carroSaida = null;
Altere a implementação do método onCreate para instanciar um novo objeto ValetDB db, consultar
todos os veículos no valets e montar a ListView.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getBaseContext(), ValetActivity.class);
startActivityForResult(intent, 1);
}
});
df = android.text.format.DateFormat.getDateFormat(getApplicationContext());
hf = android.text.format.DateFormat.getTimeFormat(getApplicationContext());
db = new ValetDB(this);
valets = db.consultarVeiculosValet();
for (Valet v : valets) {
carros.add(v.getModelo() + " - " + v.getPlaca() + "\nEntrada: "
+ df.format(v.getEntrada()) + " " + hf.format(v.getEntrada()));
Persistindo dados com SQLite
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
97
}
ListView lista = (ListView) findViewById(R.id.lista);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, carros);
lista.setAdapter(adapter);
lista.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
carroSaida = valets.get(position);
dialog = confirmarSaida();
dialog.show();
}
});
}
Altere agora o método onActivityResult para criar o objeto Valet e chamar o método salvarValet
do ValetDB:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Activity.RESULT_OK == resultCode) {
Valet valet = new Valet();
valet.setModelo(data.getStringExtra("modelo"));
valet.setPlaca(data.getStringExtra("placa"));
valet.setEntrada(new Date());
db.salvarValet(valet);
valets.add(valet);
carros.add(valet.getModelo() + " - " +
valet.getPlaca() + "\nEntrada: " +
df.format(valet.getEntrada()) + " " +
hf.format(valet.getEntrada()));
adapter.notifyDataSetChanged();
}
}
Agora só falta atualizar quando o veículo sair do valet, para isso altere o método confirmarSaida
para informar a data de saída e o preço que deve ser pago pelo serviço de valet.
Persistindo dados com SQLite
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public AlertDialog confirmarSaida() {
carroSaida.setSaida(new Date());
carroSaida.setPreco(calcularPreco(carroSaida));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.saida);
builder.setMessage("Saída do " + carroSaida.getModelo() + " - "
+ carroSaida.getPlaca() + "\nPreço: " + carroSaida.getPreco());
builder.setPositiveButton(getString(R.string.sim),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
db.salvarValet(carroSaida);
valets.remove(carroSaida);
carros.remove(carroSaida.getModelo() + " - " + carroSaida.getPlaca()
+ "\nEntrada: " + df.format(carroSaida.getEntrada()) + " "
+ hf.format(carroSaida.getEntrada()));
adapter.notifyDataSetChanged();
}
});
builder.setNegativeButton(getString(R.string.nao), null);
return builder.create();
}
Vamos também criar o método que calcula o preço que deve ser pago pelo serviço de valet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public double calcularPreco(Valet valet) {
double preco = 0;
long tempo = (valet.getSaida().getTime() - valet.getEntrada().getTime())
/ 1000 / 60;
long horas = tempo / 60;
long minutos = tempo % 60;
if (horas > 0) {
preco = horas * 3;
}
if (minutos > 0) {
preco += 3;
}
98
99
Persistindo dados com SQLite
18
19
return preco;
}
Execute novamente, o aplicativo agora está salvando, consultando e atualizando um valet, aproveite
para fechar e abrir o aplicativo para verificar se os dados foram salvos.
Figura 6.2.1 - Saída do veículo.
Neste exemplo não foi necessário remover um registro do SQLite, mas se fosse necessário o código
seria assim:
1
2
3
4
5
6
public void remover(Long idValet) {
SQLiteDatabase db = getWritableDatabase();
String where [] = new String[] { String.valueOf(idValet) };
db.delete("Valet", "_id = ?", where);
db.close();
}
O método remover recebe qual o id do valet deve ser removido e usa o método delete do db para
executar a operação no SQLite.
Persistindo dados com SQLite
100
6.3 Resumo
Neste capítulo foi implementado a persistência dos veículos no aplicativo de Valets utilizando o
SQLite.
No próximo capítulo veremos como criar uma lista personalizada, que permite organizar os
componentes nela da forma que quisermos.
6.4 Exercícios
Exercício 1 - Altere o exercício de Tarefas criado no exercício 1 do capítulo 5 para também utilizar
a persistência dos dados com o SQLite.
7. ListView personalizada
Neste capítulo vamos criar um aplicativo chamado Meus Produtos, a ideia é mostrar uma ListView
personalizada com vários produtos e conforme for alterando a quantidade de cada item da lista já
atualiza o valor total.
Figura 7.1 - Meus Produtos.
Na criação do projeto utilize uma Empty Activity, como mostrado nos primeiros capítulos. Após
criar o projeto, vamos começar criando uma classe para representar o Produto, neste exemplo será
utilizado doces como produto, mas você pode modificar e utilizar qualquer outro produto:
1
2
3
4
5
6
7
8
9
10
11
package com.leanpub.googleandroid.meusprodutos;
public class Produto {
private String nome;
private int imagem;
private Double preco;
private int quantidade;
public Produto(String nome, int imagem, Double preco) {
this.nome = nome;
this.imagem = imagem;
ListView personalizada
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
102
this.preco = preco;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public int getImagem() {
return imagem;
}
public void setImagem(int imagem) {
this.imagem = imagem;
}
public Double getPreco() {
return preco;
}
public void setPreco(Double preco) {
this.preco = preco;
}
public int getQuantidade() {
return quantidade;
}
public void setQuantidade(int quantidade) {
this.quantidade = quantidade;
}
public double getPrecoTotal() {
return preco * quantidade;
}
}
A imagem será representada como int no objeto Produto, porque será utilizado a referência interna
das figuras que estão na pasta drawable no projeto.
ListView personalizada
103
Neste exemplo foi utilizado as figuras brigadeiros.png, cupcake.png e mousse_maracuja.png que estão disponíveis no GitHub¹. Faça o download dessa figuras e coloque
na pasta res → drawable.
7.1 Criando o layout da activity_main
O layout da tela activity_main.xml terá uma ListView e as TextView para mostrar o valor total.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leanpub.googleandroid.meusprodutos.MainActivity">
<ListView
android:id="@+id/produtos"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/fundo"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.ConstraintLayout
android:id="@+id/fundo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
¹https://github.com/rafaelsakurai/livro-google-android
ListView personalizada
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
android:text="@string/total"
android:textSize="30sp"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/valorTotal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_toEndOf="@id/total"
android:text="R$ 0,00"
android:textSize="30sp"
app:layout_constraintEnd_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Adicione no arquivo strings.xml a nova String:
1
<string name="total">Total:</string>
O layout da activity_main.xml ficará como mostrado na Figura 7.1.1.
104
105
ListView personalizada
Figura 7.1.1 - Layout da activity_main.
7.2 Criando o layout da ListView
Para personalizar a ListView criamos um novo layout que representa como cada linha deve ser
apresentada. Clique com o botão direito do mouse na pasta layout, escolha no menu New a opção
Layout resource file, como mostrado na Figura 7.2.1.
Figura 7.2.1 - Criando um novo Layout.
106
ListView personalizada
Na tela New Resource File defina o File name como produto e o Root element como LinearLayout,
como mostrado na Figura 2.2.2.
Figura 7.2.2 - Criando um novo Layout.
Cada linha da lista terá uma ImageView que representa a figura do produto, um TextView com o nome
do produto, um TextView com o preço e um EditText para informar a quantidade do produto, como
mostrado na figura 7.2.3:
Figura 7.2.3 - Layout que representa uma linha da ListView.
O layout ficou assim:
ListView personalizada
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imagem"
android:layout_width="0dp"
android:layout_height="80dp"
android:src="@drawable/brigadeiros"
android:layout_marginRight="8dp"
android:layout_weight="1"/>
<LinearLayout
android:id="@+id/descricao"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginRight="8dp"
android:layout_weight="2"
android:layout_gravity="center_vertical">
<TextView
android:id="@+id/nome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Brigadeiro"
android:textSize="20sp"/>
<TextView
android:id="@+id/preco"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="R$ 0,90"
android:textSize="15sp"/>
</LinearLayout>
<EditText
android:id="@+id/quantidade"
android:layout_width="0dp"
android:layout_height="80dp"
107
ListView personalizada
43
44
45
46
108
android:inputType="number"
android:textSize="20sp"
android:layout_weight="1"/>
</LinearLayout>
Para alinhar os componentes um ao lado do outro usando tamanhos proporcionais está sendo utilizado a propriedade android:layout_weight. Esta propriedade calculo o tamanho dos componentes
com base no seu peso, neste exemplo para a ImageView e EditText foi utilizado peso 1 e para o
RelativeLayout que está no meio foi utilizado peso 2, portanto o peso total é 4, então a organização
será respectivamente 1/4, 1/4 e 2/4 do comprimento da tela, não importando o tamanho da tela.
7.3 Adicionando o comportamento da ListView
Até agora, foi montado o layout da tela e da ListView, então vamos criar o comportamento. Para
representar os itens da ListView vamos criar um Adapter dos produtos, para isso vamos criar a classe
ProdutoArrayAdapter que herda de android.widget.ArrayAdapter, como mostrado a seguir:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.leanpub.googleandroid.meusprodutos;
import
import
import
import
import
import
import
import
import
import
import
android.content.Context;
android.text.Editable;
android.text.TextWatcher;
android.util.Log;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.ArrayAdapter;
android.widget.EditText;
android.widget.ImageView;
android.widget.TextView;
import java.util.List;
public class ProdutoArrayAdapter extends ArrayAdapter<Produto> {
private Context context;
private List<Produto> produtos;
public ProdutoArrayAdapter(Context context, int resource,
List<Produto> objects) {
super(context, resource, objects);
this.context = context;
ListView personalizada
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
this.produtos = objects;
}
@Override
public View getView(final int position, View convertView,
final ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View linha = inflater.inflate(R.layout.produto, parent, false);
TextView nome = (TextView) linha.findViewById(R.id.nome);
TextView preco = (TextView) linha.findViewById(R.id.preco);
ImageView imagem = (ImageView) linha.findViewById(R.id.imagem);
EditText qtd = (EditText) linha.findViewById(R.id.quantidade);
qtd.addTextChangedListener(new TextWatcher() {
Produto p = produtos.get(position);
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
@Override
public void afterTextChanged(Editable s) {
try {
p.setQuantidade(Integer.valueOf(s.toString()));
} catch (NumberFormatException nfe) {
p.setQuantidade(0);
}
atualizar(((TextView) parent.getRootView()
.findViewById(R.id.valorTotal)), produtos);
}
});
109
ListView personalizada
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
110
Produto p = produtos.get(position);
nome.setText(p.getNome());
preco.setText("R$: " + p.getPreco());
imagem.setImageResource(p.getImagem());
return linha;
}
public void atualizar(TextView valorTotal, List<Produto> produtos) {
double total = 0;
for(Produto p : produtos) {
total += p.getPrecoTotal();
}
valorTotal.setText("R$" + total);
}
}
Na classe ProdutoArrayAdapter foi criado:
• Um construtor public ProdutoArrayAdapter(Context context, int resource, List<Produto>
objects) para receber principalmente o context que servirá para definir o layout de cada
linha da ListView e também a List<Produto> que será carregado;
• Sobrescrevemos o método public View getView(final int position, View convertView,
final ViewGroup parent) que serve para pegar cada linha da ListView;
Para criar uma linha do ListView usamos o código:
1
2
3
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View linha = inflater.inflate(R.layout.produto, parent, false);
Nele dizemos para criar um LayoutInflater que serve para carregar um layout em tempo de
execução e através dele conseguimos criar uma View com o layout do produto.xml que representa
uma linha da ListView.
No campo quantidade que é um EditText foi adicionado um TextChangedListener que serve para
identificar quando seu valor for alterado.
ListView personalizada
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
111
EditText qtd = (EditText) linha.findViewById(R.id.quantidade);
qtd.addTextChangedListener(new TextWatcher() {
Produto p = produtos.get(position);
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
try {
p.setQuantidade(Integer.valueOf(s.toString()));
} catch (NumberFormatException nfe) {
p.setQuantidade(0);
}
atualizar(((TextView) parent.getRootView().findViewById(R.id.valorTotal)),
produtos);
}
});
Precisamos implementar três métodos da TextWatcher, mas neste exemplo só precisamos usar o
método public void afterTextChanged(Editable s) que serve para executar alguma lógica após
o usuário informar a quantidade.
Então, nesse exemplo vamos chamar o método public void atualizar(TextView valorTotal,
List<Produto> produtos) que foi implementado para calcular o valor total com base nas quantidades de cada produto escolhido e depois atualiza o TextView que mostra o valor total na tela.
Agora só falta implementar a Activity como mostrado a seguir:
ListView personalizada
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
112
package com.leanpub.googleandroid.meusprodutos;
import
import
import
import
import
import
import
import
import
import
android.content.Intent;
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.widget.ListView;
android.widget.TextView;
java.util.ArrayList;
java.util.List;
public class MainActivity extends AppCompatActivity {
private List<Produto> produtos = new ArrayList();
private ProdutoArrayAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
produtos.add(new Produto("Brigadeiro", R.drawable.brigadeiros, 0.90));
produtos.add(new Produto("Cupcake", R.drawable.cupcake, 2.50));
produtos.add(new Produto("Mousse", R.drawable.mousse_maracuja, 1.50));
adapter = new ProdutoArrayAdapter(this, 0, produtos);
((ListView) findViewById(R.id.produtos)).setAdapter(adapter);
((TextView) findViewById(R.id.valorTotal)).setText("R$0.0");
}
}
Na MainActivity foi criado uma lista dos produtos que serão apresentados e também um ProdutoArrayAdapter que foi adicionado na ListView da tela.
Pronto, execute o aplicativo e preencha alguns valores nas quantidades dos produtos e veja o calculo
automático do valor total.
7.4 Resumo
Nesse capítulo abordamos como criar uma ListView personalizada, a ideia principal é que cada
linha da lista é representado por um arquivo xml de layout, portanto podemos criar qualquer tipo
ListView personalizada
113
de layout personalizado.
7.5 Exercícios
Exercício 1 - Altere o exemplo apresentado para salvar usando o SQLite as vendas.
Exercício 2 - Continuando o exercício 1, adicione mais uma tela para consultar todas as vendas
realizadas.
8. Chamando Web Service REST
Vamos continuar o aplicativo Meus Produtos criado no capítulo anterior. Até o momento criamos
uma tela que mostra os produtos, mas todos os dados são fixos no programa.
Na classe MainActivity temos a criação de três produtos que são apresentados na lista:
1
2
3
produtos.add(new Produto("Brigadeiro", R.drawable.brigadeiros, 0.90));
produtos.add(new Produto("Cupcake", R.drawable.cupcake, 2.50));
produtos.add(new Produto("Mousse", R.drawable.mousse_maracuja, 1.50));
Nesse capítulo vamos armazenar os dados dos produtos em um Web Service REST.
Figura 8.1 - Meus Produtos.
8.1 Introdução sobre Web Service REST
Web Service é uma forma de integração entre aplicações que permite a troca de dados independente
da linguagem de programação de cada aplicação.
Nesse exemplo será utilizado o OpenWS (https://openws.org/¹) que é uma plataforma quer permite
armazenar objetos JSON de forma bem simples e gratuita utilizando Web Services REST.
¹https://openws.org/
115
Chamando Web Service REST
Figura 8.1.1 - OpenWS - https://openws.org/.
Esse site é útil para criar um protótipos rápido podendo armazenar dinamicamente os dados do app
desenvolvido em um Web Service REST. Só um detalhe, todos os objetos armazenados nesse site são
públicos, então qualquer pessoa pode consultar, adicionar ou alterar as informações.
O OpenWS será utilizado para armazenar os produtos cadastrados no aplicativo Meus Produtos.
Nele será salvo objetos no formato JSON contendo os dados dos produtos que o app deve apresentar.
8.2 Alterando o aplicativo Meus Produtos
No aplicativo Meus Produtos que criado no capítulo anterior há uma tela para mostrar e calcular o
preço total dos produtos.
116
Chamando Web Service REST
Figura 8.2.1 - Meus Produtos.
Nessa tela vamos adicionar um menu no canto superior direito. Clique com o botão direito do mouse
em res e escolha no menu New a opção Directory, como mostrado na Figura 8.2.2.
Figura 8.2.2 - Criando um novo diretório.
Na tela New Directory defina o nome como menu, como mostrado na Figura 8.2.3.
117
Chamando Web Service REST
Figura 8.2.3 - Criando um novo diretório.
Clique com o botão direito na pasta menu que acabamos de criar e escolha no menu New a opção
Menu resource file, como mostrado na Figura 8.2.4.
Figura 8.2.4 - Criando um novo menu.
Na tela New Resource File defina o File name como menu_main, como mostrado na Figura 8.2.5.
118
Chamando Web Service REST
Figura 8.2.5 - Criando um novo menu.
No menu_main, altere seu código para incluir um novo item de menu, como mostrado a seguir:
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menuNovoProduto"
android:title="@string/novo_produto"
android:orderInCategory="1"
app:showAsAction="never"
android:onClick="novoProduto"/>
</menu>
Este menu é apresentado quando o usuário clica no símbolo menu que tem no canto superior da
aplicação. Nele definimos o id, título, ordem que deve aparecer no menu, o valor never na opção
app:showAsAction indica para abrir nesse menu chamado Modo de ação contextual, e o onClick
indica qual método deve ser executado quando acionar este item do menu.
Figura 8.6 - Menu de novo produto.
No arquivo strings.xml adicione a string:
Chamando Web Service REST
1
119
<string name="novo_produto">Novo Produto</string>
Na MainActivity.java adicione os imports para utilizar o menu:
1
2
import android.view.Menu;
import android.view.MenuItem;
Após o método onCreate, sobrescreva o método onCreateOptionsMenu, esse método será responsável por abrir o menu no canto superior direito da tela do app.
1
2
3
4
5
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
Ainda na MainActivity.java adicione o método novoProduto, esse método será responsável por
abrir a nova tela:
1
2
3
public void novoProduto(MenuItem item) {
\\TODO - Ainda vamos chamar a tela de produto aqui.
}
O método novoProduto ainda não está funcional, mas logo implementaremos o código
para chamar a outra tela.
8.3 Criando a tela de cadastro de produto
Vamos criar a activity que controla os produtos, para isso crie uma classe chamada ProdutoActivity
com o seguinte código:
120
Chamando Web Service REST
1
2
3
4
5
6
7
8
9
10
11
12
package com.leanpub.googleandroid.meusprodutos;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class ProdutoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_produto);
}
}
Na ProdutoActivity está apenas definindo que é uma Activity e que controla um layout chamado
activity_produto.xml. Então, crie o layout activity_produto.xml para a tela de cadastro de um
novo produto como mostrado na Figura 8.3.1:
Figura 8.3.1 - Tela de novo produto.
O código do layout activity_produto.xml fica da seguinte forma:
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progresso"
android:layout_width="match_parent"
Chamando Web Service REST
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/textoNome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/nome"
android:textSize="30sp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/nome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/textoNome" />
<TextView
android:id="@+id/textoPreco"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/preco"
android:textSize="30sp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
app:layout_constraintTop_toBottomOf="@+id/nome"/>
<EditText
android:id="@+id/preco"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
app:layout_constraintTop_toBottomOf="@+id/textoPreco"/>
<TextView
android:id="@+id/textoImagem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/imagem"
121
Chamando Web Service REST
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
122
android:textSize="30sp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
app:layout_constraintTop_toBottomOf="@+id/preco"/>
<ImageView
android:id="@+id/imagem"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textoImagem" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:onClick="obterImagem"
android:text="@string/procurar_imagem"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@id/salvar" />
<Button
android:id="@+id/salvar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:onClick="salvar"
android:text="@string/salvar"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
No começo da activity_produto.xml, adicionamos um componente chamado ProgressBar, ele
serve para mostrar o símbolo de carregando ou uma barra de progresso. Nessa definição, a
propriedade visibility definida como gone vai deixar esse componente invisível, mas quando
Chamando Web Service REST
123
consultarmos os dados do Web Service REST, utilizaremos esse ProgressBar para sinalizar que
estamos carregando os dados da tela.
1
2
3
4
5
6
<ProgressBar
android:id="@+id/progresso"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"/>
No arquivo strings.xml adicione as strings:
1
2
3
4
5
<string
<string
<string
<string
<string
name="nome">Nome</string>
name="preco">Preço</string>
name="imagem">Imagem</string>
name="procurar_imagem">Procurar Imagem</string>
name="salvar">Salvar</string>
E para registrar a nova Activity altere o arquivo AndroidManifest.xml incluindo a seguinte tag
<activity android:name=".ProdutoActivity"/>, como mostrado a seguir:
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application ... >
<activity ...>
...
</activity>
<activity android:name=".ProdutoActivity"/>
</application>
</manifest>
Nesse momento se executar o aplicativo já tem a navegação entre as telas. O comportamento será:
na tela inicial é apresentado todos os produtos cadastrados e na tela de novo produto enviaremos os
dados do produto para o OpenWS.
8.4 Adicionando arquivos no emulador do Android
Vamos deixar o aplicativo um pouco mais realista, ao cadastrar um novo produto será escolhido uma
imagem do celular, nesse exemplo a imagem será obtida do emulador.
124
Chamando Web Service REST
Para adicionar arquivos no emulador, primeiro inicie o emulador e depois no canto inferior direito
da barra lateral, acesse a aba Device File Explorer, como mostrado na Figura 8.4.1.
Figura 8.4.1 - Abrir o Device File Explorer.
No Device File Explorer acesse o diretório storage � self � primary � DCIM, clique com o botão
direito do mouse e escolha Upload, como mostrado na Figura 8.4.2:
125
Chamando Web Service REST
Figura 8.4.2 - Adicionando arquivos no emulador.
Escolha a imagem dos três produtos que serão usados no cadastro do produto, nesse exemplo foram
adicionando os arquivos brigadeiro.png, cupcake.png e mousse_maracuja.png, como mostrado na
Figura 8.4.2.
Figura 8.4.3 - Figuras adicionadas no emulador.
Pronto as imagens já estarão disponíveis para serem acessadas no emulador.
8.5 Integração com Web Service REST
Agora vamos fazer o aplicativo usar o Web Service REST disponibilizado pelo OpenWS.
Para que a aplicação possa realizar uma conexão HTTP, ela precisa ter acesso a Internet, e para
obter as imagens da galeria é necessário ter uma permissão de escrita no storage. Para permitir
isso edite o arquivo AndroidManifest.xml, adicionando as permissões antes da declaração da tag
<application>:
1
2
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
O Web Service REST será usado para enviar e obter dados no formato JavaScript Object Notation
(JSON), para facilitar a criação de objetos JSON e converter para objetos Java, vamos utilizar uma
biblioteca do Google chamado GSon.
Chamando Web Service REST
126
Os projetos Android utilizam a ferramenta Gradle para gerenciar suas dependências, isso significa
que podemos informar para o Gradle que agora o projeto precisa usar a biblioteca GSon.
Para isso edite o arquivo Gradle Scripts → build.gradle (Module: app), esse arquivo contém as
configurações para compilar o projeto Android, na propriedade dependencies inclua também:
compile 'com.google.code.gson:gson:2.8.0'
Essa linha indica que o projeto tem uma dependência com o GSon na versão 2.8.0. Altere a classe
Produto como mostrado a seguir:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.leanpub.googleandroid.meusprodutos;
public class Produto {
private String _id;
private String nome;
private String imagem;
private Double preco;
private int quantidade;
public Produto(String id, String nome, String imagem, Double preco) {
this._id = id;
this.nome = nome;
this.imagem = imagem;
this.preco = preco;
}
public Produto(String nome, String imagem, Double preco) {
this.nome = nome;
this.imagem = imagem;
this.preco = preco;
}
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getNome() {
return nome;
}
Chamando Web Service REST
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
127
public void setNome(String nome) {
this.nome = nome;
}
public String getImagem() {
return imagem;
}
public void setImagem(String imagem) {
this.imagem = imagem;
}
public Double getPreco() {
return preco;
}
public void setPreco(Double preco) {
this.preco = preco;
}
public int getQuantidade() {
return quantidade;
}
public void setQuantidade(int quantidade) {
this.quantidade = quantidade;
}
public double getPrecoTotal() {
return preco * quantidade;
}
}
Agora estamos definindo uma imagem como uma String, isso porque a imagem será enviada para
o Web Service e será salva no formato de texto.
Para utilizar o Web Service do OpenWS crie uma nova classe chamada ProdutoService, a seguir
será apresentado todo o código e logo a seguir será explicado passo a passo:
Chamando Web Service REST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.leanpub.googleandroid.meusprodutos;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import
import
import
import
import
import
import
import
import
import
import
import
java.io.BufferedInputStream;
java.io.BufferedOutputStream;
java.io.BufferedWriter;
java.io.InputStream;
java.io.OutputStream;
java.io.OutputStreamWriter;
java.io.Writer;
java.net.HttpURLConnection;
java.net.URL;
java.util.ArrayList;
java.util.List;
java.util.Scanner;
public class ProdutoService {
private static final String URL_OPEN_WS =
"https://openws.org/api/collections/";
private static final String MINHA_COLECAO = "meusprodutos";
public List<Produto> getAll() {
List<Produto> produtos = new ArrayList();
HttpURLConnection urlConnection = null;
try {
URL url = new URL(URL_OPEN_WS + MINHA_COLECAO);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
Scanner s = new Scanner(in);
String conteudo = s.useDelimiter("\\A").next();
Gson gson = new Gson();
produtos = gson.fromJson(conteudo, new TypeToken<List<Produto>>(){}
.getType());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
urlConnection.disconnect();
128
Chamando Web Service REST
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
}
return produtos;
}
public void post(Produto produto) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(URL_OPEN_WS + MINHA_COLECAO);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
OutputStream out = new BufferedOutputStream(
urlConnection.getOutputStream());
Writer w = new BufferedWriter(new OutputStreamWriter(out));
Gson gson = new Gson();
String json = gson.toJson(produto);
w.write(json);
w.close();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
Scanner s = new Scanner(in);
String conteudo = s.nextLine();
in.close();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
urlConnection.disconnect();
}
}
public void delete(String id) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(URL_OPEN_WS + MINHA_COLECAO + "/" + id);
urlConnection = (HttpURLConnection) url.openConnection();
129
Chamando Web Service REST
85
86
87
88
89
90
91
92
93
94
130
urlConnection.setRequestMethod("DELETE");
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
urlConnection.disconnect();
}
}
}
Foram declarados dois atributos, neles temos: a URL_OPEN_WS de acesso ao Web Service do
OpenWS, a MINHA_COLECAO que é o nome da coleção que o OpenWS vai criar quando enviar
os dados de um produto pelo App.
Note que no exemplo foi definido o valor da MINHA_COLECAO como meusprodutos,
quando você for definir o nome para o seu projeto, sugiro colocar algo como meusprodutos_seunome porque senão você acessará uma coleção igual ao desse exemplo e
já trará os objetos que cadastrei.
1
2
private static final String URL_OPEN_WS = "https://openws.org/api/collections/";
private static final String MINHA_COLECAO = "meusprodutos";
O método getAll() foi criado para consultar todos os produtos que estão salvos no OpenWS, nele
primeiro criamos uma conexão com a url e coleção do OpenWS:
1
2
URL url = new URL(URL + MINHA_COLECAO);
urlConnection = (HttpURLConnection) url.openConnection();
Depois executamos a chamada da URL e guardamos o retorno na variável resultado:
1
2
3
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
Scanner s = new Scanner(in);
String resultado = s.useDelimiter("\\A").next();
Esse tipo de requisição está usando o método GET do HTTP para chamar o Web Service disponibilizado pelo OpenWS, o mesmo comportamento pode ser simulado por meio de um navegador,
basta abrir o navegador e digitar na URL https://openws.org/api/collections/meusprodutos
lembrando de trocar o valor meusprodutos pelo nome que você deu para a coleção.
O resultado é um vetor de objetos JSON, então vamos utilizar a biblioteca GSon para converter essa
String para uma lista de produtos:
Chamando Web Service REST
1
2
131
Gson gson = new Gson();
produtos = gson.fromJson(resultado, new TypeToken<List<Produto>>(){}.getType());
Então, na primeira vez que chamarmos esse método não trará nada, mas após cadastrarmos algum
produto no OpenWS será retorno os produtos cadastrados.
A seguir foi criado o método post() que recebe um Produto como parâmetro e envia os dados para
o OpenWS. Agora criamos uma URL com algumas configurações diferentes:
1
2
3
4
5
6
URL url = new URL(URL + MINHA_COLECAO);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
Nessa URL é informado que a requisição deve usar o método POST do HTTP isso informa para o
Web Service salvar os dados do JSON e também é informado que o conteúdo enviado é um JSON.
A seguir é criado um Writer que é usado para enviar as informações para o Web Service. Aqui o
GSon foi utilizado para converter um objeto do tipo Produto para JSON.
1
2
3
4
5
6
7
8
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
Writer w = new BufferedWriter(new OutputStreamWriter(out));
Gson gson = new Gson();
String json = gson.toJson(produto);
w.write(json);
w.close();
E para finalizar é feita uma chamada para a URL, nessa chamada será retornado um JSON com os
dados do produto que foi salvo no OpenWS.
Também foi criado um método chamado delete() que recebe um id de produto que deve ser
removido, mas esse método não foi usado no exemplo, apenas foi implementado para que você
possa chamar esse método e limpar os dados do Web Service.
Na classe ProdutoArrayAdapter foi alterado apenas a forma como será renderizado a imagem, no
final do método getView altere o seguinte código:
1
imagem.setImageResource(p.getImagem());
Para esse:
Chamando Web Service REST
1
2
3
132
byte[] byteArray = Base64.decode(p.getImagem(), Base64.DEFAULT) ;
imagem.setImageBitmap(BitmapFactory.decodeByteArray(byteArray, 0,
byteArray.length));
Esse novo código está dizendo para pegar a String da imagem e decodificar no formado de um vetor
de bytes e depois esse vetor é decodificado no formado de imagem bitmap e guardado no ImageView.
A seguir temos o novo código da classe MainActivity e logo a seguir tem a explicação de cada
método:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.leanpub.googleandroid.meusprodutos;
import
import
import
import
import
import
import
import
import
android.content.Intent;
android.os.AsyncTask;
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.view.Menu;
android.view.MenuItem;
android.widget.ListView;
android.widget.ProgressBar;
android.widget.TextView;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ProdutoService service = new ProdutoService();
private ProdutoArrayAdapter adapter;
private ProgressBar progressBar;
private void carregarDados() {
new CarregarMeusProdutosTask().execute();
((TextView) findViewById(R.id.valorTotal)).setText("R$ 0,00");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progresso);
carregarDados();
}
@Override
Chamando Web Service REST
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
133
protected void onResume() {
super.onResume();
carregarDados();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
private class CarregarMeusProdutosTask extends AsyncTask<String, Void,
List<Produto>> {
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
}
@Override
protected void onPostExecute(List<Produto> produtos) {
if (produtos != null) {
adapter = new ProdutoArrayAdapter(getBaseContext(), 0, produtos);
((ListView) findViewById(R.id.produtos)).setAdapter(adapter);
}
progressBar.setVisibility(ProgressBar.INVISIBLE);
}
@Override
protected List<Produto> doInBackground(String... params) {
return service.getAll();
}
}
public void novoProduto(MenuItem item) {
startActivity(new Intent(this, ProdutoActivity.class));
}
}
O método carregarDados() executa uma tarefa para consultar os dados do Web Service e com o
resultado recarregar a tela:
Chamando Web Service REST
1
2
3
4
134
private void carregarDados() {
new CarregarMeusProdutosTask().execute();
((TextView) findViewById(R.id.valorTotal)).setText("R$ 0,00");
}
Quando executamos uma requisição HTTP precisamos fazer com que isso execute em uma thread
paralela, porque se a thread principal demorar muito o próprio sistema operacional do Android
interrompe o aplicativo.
A classe CarregarMeusProdutosTask foi declarada internamente dentro da classe MainActivity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private class CarregarMeusProdutosTask extends AsyncTask<String, Void,
List<Produto>> {
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
}
@Override
protected void onPostExecute(List<Produto> produtos) {
if (produtos != null) {
adapter = new ProdutoArrayAdapter(getBaseContext(), 0, produtos);
((ListView) findViewById(R.id.produtos)).setAdapter(adapter);
}
progressBar.setVisibility(ProgressBar.INVISIBLE);
}
@Override
protected List<Produto> doInBackground(String... params) {
return service.getAll();
}
}
Para executar a requisição em paralelo criamos a classe interna chamada CarregarMeusProdutosTask que estende a classe android.os.AsyncTask. E precisamos implementar os seguintes métodos:
• onPreExecute() - Método executado antes do processamento da thread;
• onPostExecute() - Método executado após o processamento da thread;
• String[] doInBackground(String … params) - Método que é executado pela thread.
Essa classe permite executar tarefas de forma assíncrona, então chamar o método execute(),
apresentará o componente ProgressBar no método onPreExecute(), depois será consultado todos
Chamando Web Service REST
135
os produtos e atualizado a ListView no método doInBackground() e para finalizar no método
onPostExecute() o ProgressBar volta a ficar invisível.
O método onCreate foi alterado para chamar o método carregarDados():
1
2
3
4
5
6
7
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progresso);
carregarDados();
}
O mesmo foi feito com o método onResume() que serve para atualizar essa tela após a criação de
um produto. Porque será chamado a tela de novo produto, e após cadastrar o produto quando voltar
para a tela principal o método onResume() será executado
1
2
3
4
5
6
@Override
protected void onResume() {
super.onResume();
carregarDados();
}
E o método novoProduto(MenuItem item) é usado para chamar a tela de cadastro de novo produto
quando clicar no menu.
1
2
3
public void novoProduto(MenuItem item) {
startActivity(new Intent(this, ProdutoActivity.class));
}
Por último altere a classe ProdutoActivity criada no inicio desse exemplo com o seguinte código:
Chamando Web Service REST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.leanpub.googleandroid.meusprodutos;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
android.Manifest;
android.app.Activity;
android.content.Intent;
android.content.pm.PackageManager;
android.database.Cursor;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.net.Uri;
android.os.AsyncTask;
android.os.Bundle;
android.provider.MediaStore;
android.support.v4.app.ActivityCompat;
android.support.v4.content.ContextCompat;
android.support.v7.app.AppCompatActivity;
android.util.Base64;
android.util.Log;
android.view.View;
android.widget.EditText;
android.widget.ImageView;
android.widget.ProgressBar;
import java.io.ByteArrayOutputStream;
public class ProdutoActivity extends AppCompatActivity {
private ProdutoService service = new ProdutoService();
private Bitmap bmp;
private String nome;
private double preco;
private String imagemURI;
private ProgressBar progresso;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_produto);
progresso = (ProgressBar) findViewById(R.id.progresso);
}
public void obterImagem(View view) {
if (ContextCompat.checkSelfPermission(this,
136
Chamando Web Service REST
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE },
1);
}
}
Intent i = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, 1);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
imagemURI = data.getDataString();
Uri uri = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(uri,filePathColumn, null,
null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
Log.d("picturePath", picturePath);
bmp = BitmapFactory.decodeFile(picturePath);
cursor.close();
ImageView imageView = (ImageView) findViewById(R.id.imagem);
imageView.setImageURI(uri);
}
}
137
Chamando Web Service REST
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
public void salvar(View view) {
nome = ((EditText) findViewById(R.id.nome)).getText().toString();
preco = Double.valueOf(((EditText) findViewById(R.id.preco))
.getText().toString());
new EnviarMeusProdutosTask().execute();
}
private class EnviarMeusProdutosTask extends AsyncTask<String, Void, Void> {
@Override
protected void onPreExecute() {
progresso.setVisibility(ProgressBar.VISIBLE);
}
@Override
protected void onPostExecute(Void aVoid) {
((EditText) findViewById(R.id.nome)).setText("");
((ImageView) findViewById(R.id.imagem)).setImageURI(null);
((EditText) findViewById(R.id.preco)).setText("");
imagemURI = "";
progresso.setVisibility(ProgressBar.INVISIBLE);
}
@Override
protected Void doInBackground(String... params) {
service.post(new Produto(nome, converterImagem(imagemURI), preco));
return null;
}
}
private String converterImagem(String path) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
return Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP);
}
}
O método obterImagem(View view) é chamado pelo botão PROCURAR IMAGEM.
138
139
Chamando Web Service REST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void obterImagem(View view) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE },
1);
}
Intent i = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, 1);
}
Esse método cria uma Intent para selecionar uma figura da galeria, como mostrado na Figura 8.5.1:
Figura 8.5.1 - Selecionar uma imagem.
Após selecionar a figura será solicitado ao usuário se ele permite o acesso para escrita nos seus
arquivos, isso é usado para gerar um bitmap da figura que ele escolheu, como mostrado na Figura
8.5.2.
140
Chamando Web Service REST
Figura 8.5.2 - Solicitação de permissão de escrita.
Depois o método onActivityResult(int requestCode, int resultCode, Intent data) será chamado passando como retorno um Intent contendo a imagem selecionada:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
imagemURI = data.getDataString();
Uri uri = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(uri,filePathColumn, null,
null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
bmp = BitmapFactory.decodeFile(picturePath);
cursor.close();
ImageView imageView = (ImageView) findViewById(R.id.imagem);
imageView.setImageURI(uri);
}
}
141
Chamando Web Service REST
Então, com a figura selecionada será atualizado o ImageView da tela, como mostrado na Figura 8.5.3:
Figura 8.5.3 - Tela de novo produto.
O método salvar(View view) será chamado pelo botão SALVAR para pegar os dados preenchidos
pelo usuário e chamar uma tarefa para salvar o produto.
1
2
3
4
5
6
7
public void salvar(View view) {
nome = ((EditText) findViewById(R.id.nome)).getText().toString();
preco = Double.valueOf(((EditText) findViewById(R.id.preco))
.getText().toString());
new EnviarMeusProdutosTask().execute();
}
A classe EnviarMeusProdutosTask foi criada para chamar o método do web service que serve para
salvar um novo produto.
Chamando Web Service REST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
142
private class EnviarMeusProdutosTask extends AsyncTask<String, Void, Void> {
@Override
protected void onPreExecute() {
progresso.setVisibility(ProgressBar.VISIBLE);
}
@Override
protected void onPostExecute(Void aVoid) {
((EditText) findViewById(R.id.nome)).setText("");
((ImageView) findViewById(R.id.imagem)).setImageURI(null);
((EditText) findViewById(R.id.preco)).setText("");
imagemURI = "";
progresso.setVisibility(ProgressBar.INVISIBLE);
}
@Override
protected Void doInBackground(String... params) {
service.post(new Produto(nome, converterImagem(imagemURI), preco));
return null;
}
}
Quando executar o EnviarMeusProdutosTask, no método onPreExecute() o ProgressBar fica
visível, depois monta um Produto e passa para salvar no Web Service no método doInBackground()
e para finalizar no método onPostExecute() o ProgressBar fica invisível.
O método converterImagem(String path) foi criado para converter a imagem selecionada para
uma String.
1
2
3
4
5
private String converterImagem() {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
return Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP);
}
Pronto, agora o aplicativo está integrado com o Web Service do OpenWS, teste as telas e cadastre
alguns produtos.
143
Chamando Web Service REST
Figura 8.5.4 - Meus Produtos.
8.6 Resumo
Nesse capítulo foi apresentado o uso de Web Services em aplicativos Android, o objetivo não é focar
apenas no uso do OpenWS, mas sim no consumo de um serviço que pode ser disponibilizado por
outra plataforma ou até mesmo criado por você.
Outro ponto importante nesse capítulo é o uso de tarefas que são executadas de forma assíncrona
utilizando a AsyncTask, porque atrás dela podemos executar algumas tarefas que consomem um
pouco mais de tempo, sem bloquear a thread principal da tela do aplicativo.
Também mostramos como: adicionar ou obter arquivos no emulador usando o Device File Explorer,
criar um Intent para obter uma figura da galeria, converter uma imagem para String usando Base64,
adicionar uma biblioteca de dependência, converter objetos Java para GSon e vice versa, fazer
requisições HTTP e apresentar uma carregando usando o componente ProgressBar.
8.7 Exercícios
Exercício 1 - Altere o projeto Meus Produtos e salve as vendas realizadas usando o OpenWS, crie
também uma tela para consultar todas as vendas realizadas.
Exercício 2 - O que você achou de guardar a imagem como String junto com os dados do Produto?
Será que isso consome muito trafego de dados? O que pode ser alterado para melhorar esse exemplo?
9. Action Bar
Nesse capítulo será abordado o uso do Action Bar como um acesso rápido de algumas tela do
aplicativo, também utilizaremos os componentes DatePicker, TimePicker e Spinner.
O Action Bar é uma barra de ações que fica localizada na parte superior na tela.
Figura 9.1 - Action Bar.
Essa área é utilizada normalmente para:
1. Apresentar o nome ou em qual local do aplicativo o usuário está;
2. Ações muito usuais e de fácil acesso;
3. Navegação por meio de menu ou abas.
Todos os projetos que criamos até o momento possuem uma Action Bar, mas basicamente estavamos
utilizando apenas a opção de menu.
O interessante é que todos os aplicativos de destaque (exceto jogos) no Google Play¹ utilizam Action
Bar, é algo que facilita muito a usabilidade do aplicativo.
Crie um novo projeto Android chamado Tá na hora. Este aplicativo servirá para anotar a quantidade
de horas gastas em tarefas que o usuário executa, pode ser usado para registrar horas de trabalho,
horas de exercícios, horas que passa jogando, enfim qualquer coisa, e vamos fazer um relatório de
resumo das atividades.
¹https://play.google.com/store
145
Action Bar
Figura 9.2 - Aplicativo Tá na hora.
9.1 Configurando o Action Bar
Por padrão quando criamos um novo projeto, e escolhemos uma Empty Activity uma Activity é
criada, mas o Action Bar apresenta apenas o nome do projeto, como mostrado na Figura 9.1.1.
Figura 9.1.1 - Action Bar padrão.
Então para criar um menu, faremos igual ao do capítulo anterior:
1) Crie o diretório menu dentro do diretório res;
2) Dentro do diretório menu crie um Menu Resource File chamado menu_main, adicione nele o
seguinte conteúdo:
146
Action Bar
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/adicionar"
android:title="@string/adicionar"
android:orderInCategory="1"
app:showAsAction="ifRoom" />
</menu>
O valor ifRoom da propriedade app:showAsAction, indica que se houver espaço este item deve
aparecer no Action Bar.
No arquivo strings.xml adicione a String:
1
<string name="adicionar">Adicionar</string>
Para esse menu aparecer no Action Bar é necessário incluir o método onCreateOptionsMenu na
MainActivity.java:
1
2
3
4
5
6
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
O método public boolean onCreateOptionsMenu(Menu menu) é usado para carregar um menu
que é o Action Bar. O menu é um arquivo XML de layout normalmente chamado menu_main.xml, sendo carregado durante a execução do aplicativo por meio do método getMenuInflater().inflate(R.menu.menu_main, menu);.
O uso da anotação @Override indica que o método está sobrescrevendo o comportamento da classe pai nesse caso a classe android.app.Activity.java, também é usado
para indicar que está implementando um método de uma interface.
A Action Bar agora terá a aparência da Figura 9.1.2:
Figura 9.1.2 - Action Bar com item de menu.
147
Action Bar
É importante notar que podemos criar menus diferentes para as telas do aplicativo.
Mas nem sempre um menu com o texto é a melhor visualmente, uma sugestão é o uso de ícones na
Action Bar, porque também permitirá criar mais menus se for necessário.
Para criar aplicativos melhores, a Google disponibilizou diversos ícones que seguem as boas práticas
de Material Design² gratuitamente no GitHub³ e podem ser utilizados em projetos Web, Android,
iOS, etc.
No site https://github.com/google/material-design-icons/⁴ tem um botão para fazer Download ZIP
com todos os ícones e em diversas fontes diferentes. Faça o download desse ZIP que utilizaremos
alguns ícones no aplicativo criado nesse capítulo.
Nesse exemplo, adicione na pasta drawable as imagens ic_alarm_add_white_48dp.png e ic_alarm_on_white_48dp.png que estão dentro da pasta material-design-icons-master → action →
drawable-xxxhdpi.
No item adicionar do menu informe o ícone android:icon="@drawable/ic_alarm_add_white_48dp", agora o Action Bar terá a aparência igual da Figura 9.1.3.
Figura 9.1.3 - Action Bar com ícone no item de menu.
Vamos continuar a criação do projeto e depois voltamos para configurar a ação deste item adicionar.
9.2 Criando o layout das telas
Na tela inicial do activity_main.xml vamos adicionar uma lista que apresentará todas as tarefas
registradas:
²https://www.google.com/design/spec/material-design/introduction.html
³https://github.com/google/material-design-icons/
⁴https://github.com/google/material-design-icons/
Action Bar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
148
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leanpub.googleandroid.tanahora.MainActivity">
<ListView
android:id="@+id/lista"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
Para representar os itens da ListView crie um novo arquivo de layout chamado tarefa.xml com o
seguinte código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
149
Action Bar
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/nome"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tempo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="02:40"
android:textSize="30sp"
android:textStyle="bold" />
<TextView
android:id="@+id/categoria"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="Trabalho" />
</LinearLayout>
<TextView
android:id="@+id/nome"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:gravity="top"
android:text="Escrever o capítulo 09 do livro Google Android: Uma aborda\
gem prática."
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="3"
app:layout_constraintStart_toEndOf="@+id/linearLayout"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Nesse layout tem uma linha mostrando o tempo usado e a categoria da tarefa e outra linha mostrando
o nome da tarefa, como mostrado na figura a seguir:
150
Action Bar
Figura 9.2.1 - Representação da linha da ListView.
E para cadastrar as tarefas crie um novo layout chamado activity_tarefa.xml com o seguinte
código:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp">
<TextView
android:id="@+id/textoTarefa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/tarefa"
android:textSize="25sp"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/nome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="@string/hintTarefa"
app:layout_constraintTop_toBottomOf="@id/textoTarefa" />
<TextView
android:id="@+id/textoInicio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/inicio"
android:textSize="25sp"
app:layout_constraintTop_toBottomOf="@id/nome"/>
<LinearLayout
android:id="@+id/inicio"
Action Bar
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/textoInicio">
<EditText
android:id="@+id/dataInicio"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="date"
android:hint="@string/hintDataInicio"
android:layout_weight="2"/>
<EditText
android:id="@+id/horaInicio"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="time"
android:hint="@string/hintHoraInicio"
android:layout_weight="1"/>
</LinearLayout>
<TextView
android:id="@+id/textoFim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fim"
android:textSize="25sp"
app:layout_constraintTop_toBottomOf="@id/inicio"/>
<LinearLayout
android:id="@+id/fim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/textoFim">
<EditText
android:id="@+id/dataFim"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="date"
android:hint="@string/hintDataFim"
android:layout_weight="2"/>
<EditText
151
Action Bar
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
152
android:id="@+id/horaFim"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="time"
android:hint="@string/hintHoraFim"
android:layout_weight="1"/>
</LinearLayout>
<TextView
android:id="@+id/textoCategoria"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/categoria"
android:textSize="25sp"
app:layout_constraintTop_toBottomOf="@id/fim"/>
<Spinner
android:id="@+id/categoria"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/categorias"
app:layout_constraintTop_toBottomOf="@id/textoCategoria"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:onClick="salvar"
android:text="@string/salvar"
android:textSize="25sp"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
Nesse layout estamos definindo os campos para o usuário informar o nome da tarefa, data / hora de
inicio, data / hora fim e uma categoria. Para auxiliar o preenchimento, nos EditText foi utilizado o
android:hint como uma dica do que pode ser preenchido no campo.
Para representar a categoria será utilizado o componente Spinner, nele usamos a propriedade
android:entries="@array/categorias" para usar um conjunto fixo de itens representado por uma
array de strings.
No arquivo strings.xml adicione essas strings:
153
Action Bar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<string name="tarefa">Tarefa</string>
<string name="inicio">Inicio</string>
<string name="fim">Fim</string>
<string name="categoria">Categoria</string>
<string name="salvar">Salvar</string>
<array name="categorias">
<item>Diversão</item>
<item>Estudos</item>
<item>Trabalho</item>
</array>
<string name="hintTarefa">Descrição breve da tarefa.</string>
<string name="hintDataInicio">28/02/2020</string>
<string name="hintHoraInicio">09:00</string>
<string name="hintDataFim">28/02/2020</string>
<string name="hintHoraFim">10:30</string>
O layout da tela de tarefa ficará como a Figura 9.2.2 a seguir:
Figura 9.2.2 - Layout da tela de tarefas.
Action Bar
9.3 Implementando o comportamento do aplicativo
Para começar vamos criar uma classe que representa a tarefa:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.leanpub.googleandroid.tanahora;
import java.util.Date;
public class Tarefa {
private Long id;
private String nome;
private Date inicio;
private Date fim;
private String categoria;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public Date getInicio() {
return inicio;
}
public void setInicio(Date inicio) {
this.inicio = inicio;
}
public Date getFim() {
return fim;
}
154
Action Bar
39
40
41
42
43
44
45
46
47
48
49
50
51
155
public void setFim(Date fim) {
this.fim = fim;
}
public String getCategoria() {
return categoria;
}
public void setCategoria(String categoria) {
this.categoria = categoria;
}
}
Um detalhe importante é que na classe Tarefa.java temos os campos inicio e fim que representam
a data/hora da atividade, mas na tela essas informações serão separados em campos especificos para
data e hora.
Os dados serão salvos no SQLite, então vamos criar a classe TarefaDB:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.leanpub.googleandroid.tanahora;
import
import
import
import
import
android.content.ContentValues;
android.content.Context;
android.database.Cursor;
android.database.sqlite.SQLiteDatabase;
android.database.sqlite.SQLiteOpenHelper;
import
import
import
import
java.text.DateFormat;
java.text.SimpleDateFormat;
java.util.ArrayList;
java.util.List;
public class TarefaDB extends SQLiteOpenHelper {
private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public TarefaDB(Context context) {
super(context, "TarefaDB", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE Tarefa (_id INTEGER PRIMARY KEY AUTOINCREMENT, nome\
Action Bar
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
TEXT, inicio TEXT, fim TEXT, categoria TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public Tarefa salvar(Tarefa tarefa) {
SQLiteDatabase db = getWritableDatabase();
try {
ContentValues values = new ContentValues();
values.put("nome", tarefa.getNome());
values.put("inicio", df.format(tarefa.getInicio()));
values.put("fim", df.format(tarefa.getFim()));
values.put("categoria", tarefa.getCategoria());
if (tarefa.getId() == null) {
long id = db.insert("Tarefa", null, values);
tarefa.setId(id);
} else {
String[] where = new String[]{String.valueOf(tarefa.getId())};
db.update("Tarefa", values, "_id = ?", where);
}
} finally {
db.close();
}
return tarefa;
}
public Tarefa consultarTarefaPorId(Long id) {
Tarefa tarefa = new Tarefa();
SQLiteDatabase db = getReadableDatabase();
try {
Cursor cursor = db.rawQuery("SELECT _id, nome, inicio, fim,
categoria FROM Tarefa where _id = ?", new String[]{id.toString()});
cursor.moveToFirst();
tarefa.setId(cursor.getLong(0));
tarefa.setNome(cursor.getString(1));
156
Action Bar
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
tarefa.setInicio(df.parse(cursor.getString(2)));
tarefa.setFim(df.parse(cursor.getString(3)));
tarefa.setCategoria(cursor.getString(4));
cursor.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
db.close();
}
return tarefa;
}
public List<Tarefa> consultarTarefas() {
List<Tarefa> lista = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
try {
Cursor cursor = db.rawQuery("SELECT _id, nome, inicio, fim,
categoria FROM Tarefa", null);
cursor.moveToFirst();
for (int i = 0; i < cursor.getCount(); i++) {
Tarefa tarefa = new Tarefa();
tarefa.setId(cursor.getLong(0));
tarefa.setNome(cursor.getString(1));
tarefa.setInicio(df.parse(cursor.getString(2)));
tarefa.setFim(df.parse(cursor.getString(3)));
tarefa.setCategoria(cursor.getString(4));
lista.add(tarefa);
cursor.moveToNext();
}
cursor.close();
} catch(Exception e) {
e.printStackTrace();
} finally {
db.close();
}
return lista;
}
157
Action Bar
108
158
}
A classe TarefaDB tem métodos para salvar, alterar, consultarTarefaPorId e consultarTarefas.
Note no método consultarTarefaPorId que a consulta recebe um parâmetro:
1
2
Cursor cursor = db.rawQuery("SELECT _id, nome, inicio, fim, categoria FROM
Tarefa where _id = ?", new String[]{id.toString()});
Podemos passar diversos parâmetros para uma consulta por meio do uso das ? (interrogações), cada
? representa um parâmetro da consulta e seus valores são passados para a consulta através de um
array de Strings.
Agora vamos criar um adaptador para a ListView chamado TarefaAdapter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.leanpub.googleandroid.tanahora;
import
import
import
import
import
import
android.content.Context;
android.view.LayoutInflater;
android.view.View;
android.view.ViewGroup;
android.widget.ArrayAdapter;
android.widget.TextView;
import java.util.List;
public class TarefaAdapter extends ArrayAdapter<Tarefa> {
private Context context;
private List<Tarefa> tarefas;
public TarefaAdapter(Context context, int resource, List<Tarefa> objects) {
super(context, resource, objects);
this.context = context;
this.tarefas = objects;
}
@Override
public View getView(final int position, View convertView,
final ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View linha = inflater.inflate(R.layout.tarefa, parent, false);
Action Bar
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
159
TextView tempo = (TextView) linha.findViewById(R.id.tempo);
TextView nome = (TextView) linha.findViewById(R.id.nome);
TextView categoria = (TextView) linha.findViewById(R.id.categoria);
Tarefa p = tarefas.get(position);
long time = (p.getFim().getTime() - p.getInicio().getTime()) / 1000 / 60;
long minutos = time % 60;
long horas = time / 60;
tempo.setText((horas < 10 ? "0" + horas : horas) + ":"
+ (minutos < 10 ? "0" + minutos : minutos));
nome.setText(p.getNome());
categoria.setText(p.getCategoria());
return linha;
}
}
Na TarefaAdapter estamos simplesmente recebendo uma lista de objetos tarefas e populando a
ListView com o layout criado na tarefa.xml.
Agora vamos criar uma activity para controlar a tela de cadastro de tarefa, para isso crie a classe
TarefaActivity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.leanpub.googleandroid.tanahora;
import
import
import
import
import
import
import
import
import
android.app.DatePickerDialog;
android.app.TimePickerDialog;
android.os.Bundle;
android.support.v7.app.AppCompatActivity;
android.view.View;
android.widget.DatePicker;
android.widget.EditText;
android.widget.Spinner;
android.widget.TimePicker;
import
import
import
import
java.text.DateFormat;
java.text.SimpleDateFormat;
java.util.Calendar;
java.util.Date;
public class TarefaActivity extends AppCompatActivity {
160
Action Bar
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
private
private
private
private
private
DateFormat dhf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
DateFormat hf = new SimpleDateFormat("HH:mm");
Long tarefaId;
TarefaDB db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tarefa);
db = new TarefaDB(this);
if (getIntent().getExtras() != null) {
tarefaId = getIntent().getExtras().getLong("id");
}
Calendar inicio = Calendar.getInstance();
Calendar fim = Calendar.getInstance();
if (tarefaId == null) {
fim.set(Calendar.HOUR_OF_DAY, fim.get(Calendar.HOUR_OF_DAY) + 1);
} else {
TarefaDB db = new TarefaDB(this);
Tarefa tarefa = db.consultarTarefaPorId(tarefaId);
((EditText) findViewById(R.id.nome)).setText(tarefa.getNome());
inicio.setTime(tarefa.getInicio());
fim.setTime(tarefa.getFim());
Spinner categoria = ((Spinner) findViewById(R.id.categoria));
int pos = 0;
String[] categorias = getResources().getStringArray(R.array.categorias);
for (int i = 0; i < categorias.length; i++) {
String s = categorias[i];
if (s.equals(tarefa.getCategoria())) {
pos = i;
break;
}
}
categoria.setSelection(pos);
}
adicionarDatePicker((EditText) findViewById(R.id.dataInicio), inicio);
Action Bar
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
adicionarDatePicker((EditText) findViewById(R.id.dataFim), fim);
adicionarTimePicker((EditText) findViewById(R.id.horaInicio), inicio);
adicionarTimePicker((EditText) findViewById(R.id.horaFim), fim);
}
private void adicionarDatePicker(final EditText edit, final Calendar date) {
edit.setText(df.format(date.getTime()));
final DatePickerDialog.OnDateSetListener dpd =
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
date.set(Calendar.YEAR, year);
date.set(Calendar.MONTH, monthOfYear);
date.set(Calendar.DAY_OF_MONTH, dayOfMonth);
edit.setText(df.format(date.getTime()));
}
};
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DatePickerDialog(TarefaActivity.this, dpd, date.get(Calendar.YEAR),
date.get(Calendar.MONTH), date.get(Calendar.DAY_OF_MONTH)).show();
}
});
}
private void adicionarTimePicker(final EditText edit, final Calendar date) {
edit.setText(hf.format(date.getTime()));
final TimePickerDialog.OnTimeSetListener tpd
= new TimePickerDialog.OnTimeSetListener(){
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
date.set(Calendar.HOUR_OF_DAY, hourOfDay);
date.set(Calendar.MINUTE, minute);
edit.setText(hf.format(date.getTime()));
}
};
161
Action Bar
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new TimePickerDialog(TarefaActivity.this, tpd,
date.get(Calendar.HOUR_OF_DAY), date.get(Calendar.MINUTE)
, true).show();
}
});
}
public void salvar(View view) {
Date inicio;
Date fim;
try{
inicio = dhf.parse(
((EditText) findViewById(R.id.dataInicio)).getText().toString() + " "
+ ((EditText) findViewById(R.id.horaInicio)).getText().toString());
fim = dhf.parse(
((EditText) findViewById(R.id.dataFim)).getText().toString() + " "
+ ((EditText) findViewById(R.id.horaFim)).getText().toString());
} catch(Exception e) {
inicio = new Date();
fim = new Date();
}
Tarefa tarefa = new Tarefa();
tarefa.setId(tarefaId);
tarefa.setNome(((EditText) findViewById(R.id.nome)).getText().toString());
tarefa.setInicio(inicio);
tarefa.setFim(fim);
tarefa.setCategoria((String) ((Spinner) findViewById(R.id.categoria))
.getSelectedItem());
db.salvar(tarefa);
finish();
}
}
Também adicione a TarefaActivity no AndroidManifest:
162
Action Bar
1
163
<activity android:name=".TarefaActivity"/>
No método onCreate verificamos se ao chamar essa activity está sendo enviado um id da tarefa, isso
é usado para quando receber um id, então consulta os dados da tarefa porque está querendo alterar
os dados de uma tarefa já previamente criada.
Para obter e selecionar um item do Spinner usamos o método setSelection passando a posição do
elemento selecionado:
1
2
Spinner categoria = ((Spinner) findViewById(R.id.categoria));
categoria.setSelection(pos);
Lembrando que a posição do primeiro elemento é zero (0).
Para obter o array de strings declarado no arquivo strings.xml usamos o seguinte código:
1
String[] categorias = getResources().getStringArray(R.array.categorias);
O método adicionarDatePicker implementa uma caixa de dialógo que permite selecionar uma data
quando clicar no EditText da data de inicio e fim da tarefa.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void adicionarDatePicker(final EditText edit, final Calendar date) {
edit.setText(df.format(date.getTime()));
final DatePickerDialog.OnDateSetListener dpd
= new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
date.set(Calendar.YEAR, year);
date.set(Calendar.MONTH, monthOfYear);
date.set(Calendar.DAY_OF_MONTH, dayOfMonth);
edit.setText(df.format(date.getTime()));
}
};
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DatePickerDialog(TarefaActivity.this, dpd, date.get(Calendar.YEAR),
date.get(Calendar.MONTH), date.get(Calendar.DAY_OF_MONTH)).show();
}
});
}
Action Bar
164
O método adicionarDatePicker recebe um EditText que guarda o valor da data e também um
Calendar que possui e guarda a data que é apresentada no DatePicker, o uso do Calendar é apenas
para facilitar a separação dos campos dia, mês e ano.
O método adicionarTimePicker implementa uma caixa de dialogo que permite selecionar uma hora
quando clicar no EditText da hora de inicio e fim da tarefa.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void adicionarTimePicker(final EditText edit, final Calendar date) {
edit.setText(hf.format(date.getTime()));
final TimePickerDialog.OnTimeSetListener tpd
= new TimePickerDialog.OnTimeSetListener(){
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
date.set(Calendar.HOUR_OF_DAY, hourOfDay);
date.set(Calendar.MINUTE, minute);
edit.setText(hf.format(date.getTime()));
}
};
edit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new TimePickerDialog(TarefaActivity.this, tpd,
date.get(Calendar.HOUR_OF_DAY), date.get(Calendar.MINUTE), true).show();
}
});
}
O método adicionarTimePicker recebe um EditText que guarda o valor da hora e também um
Calendar que possui e guarda a hora que é apresentada no TimePicker, o uso do Calendar é apenas
para facilitar a separação dos campos hora e minuto.
A seguir temos as imagens das caixas de dialógo de data e hora na versão antiga (figura 9.6) e na
versão nova (figura 9.7):
165
Action Bar
Figura 9.6 - Antiga versão do DataPicker e TimePicker.
Figura 9.7 - Nova versão do DataPicker e TimePicker.
Por fim temos o método salvar que salva os dados no SQLite:
Action Bar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
166
public void salvar(View view) {
Date inicio;
Date fim;
try{
inicio = dhf.parse(
((EditText) findViewById(R.id.dataInicio)).getText().toString() + " "
+ ((EditText) findViewById(R.id.horaInicio)).getText().toString());
fim = dhf.parse(
((EditText) findViewById(R.id.dataFim)).getText().toString() + " "
+ ((EditText) findViewById(R.id.horaFim)).getText().toString());
} catch(Exception e) {
inicio = new Date();
fim = new Date();
}
Tarefa tarefa = new Tarefa();
tarefa.setId(tarefaId);
tarefa.setNome(((EditText) findViewById(R.id.nome)).getText().toString());
tarefa.setInicio(inicio);
tarefa.setFim(fim);
tarefa.setCategoria((String) ((Spinner) findViewById(R.id.categoria))
.getSelectedItem());
db.salvar(tarefa);
finish();
}
Note que ao final do salvar é chamado o método finish();, essa chamada indica que a Activity
deve ser encerrada e portanto retornará para a MainActivity.
Na MainActivity vamos carregar a lista de acordo com as tarefas que foram salvas no SQLite:
1
2
3
4
5
6
7
8
9
10
11
package com.leanpub.googleandroid.tanahora;
import
import
import
import
import
import
import
import
android.content.Intent;
android.support.v7.app.AppCompatActivity;
android.os.Bundle;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.widget.AdapterView;
android.widget.ListView;
Action Bar
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.util.List;
public class MainActivity extends AppCompatActivity {
private TarefaDB db;
private List<Tarefa> lista;
private TarefaAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = new TarefaDB(this);
carregarLista();
ListView list = (ListView) findViewById(R.id.lista);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Bundle bundle = new Bundle();
bundle.putLong("id", lista.get(position).getId());
Intent i = new Intent(getBaseContext(), TarefaActivity.class);
i.putExtras(bundle);
startActivity(i);
}
});
}
@Override
protected void onResume() {
super.onResume();
carregarLista();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
167
168
Action Bar
54
55
56
57
58
59
60
61
62
63
64
65
public void carregarLista() {
lista = db.consultarTarefas();
ListView list = (ListView) findViewById(R.id.lista);
adapter = new TarefaAdapter(this, 0, lista);
list.setAdapter(adapter);
}
public void novo(MenuItem item) {
startActivity(new Intent(this, TarefaActivity.class));
}
}
No menu_main.xml adicione no item adicionar a ação do onClick, para quando clicar nele abrir a
tela de cadastro de tarefa:
1
android:onClick="novo"
Execute o aplicativo e teste o cadastro e alteração das tarefas:
Figura 9.3.1 - Executando o aplicativo Tá na hora.
169
Action Bar
9.4 Alterando o ícone do aplicativo
No AndroidManifest.xml podemos informar qual o ícone utilizado por meio da propriedade
android:icon="@mipmap/ic_launcher" se quiser mudar o ícone basta alterar as imagens que estão
na pasta res:
mipmap-mdpi - Imagem com tamanho 48x48.
mipmap-hdpi - Imagem com tamanho 72x72.
mipmap-xhdpi - Imagem com tamanho 96x96.
mipmap-xxhdpi - Imagem com tamanho 144x144.
mipmap-xxxhdpi - Imagem com tamanho 192x192.
Figura 9.4.1 - Ícone do aplicativo.
9.5 Resumo
Nesse capítulo abordamos o uso básico do Action Bar para criar acesso rápido as telas do aplicativo,
também usamos os componentes Spinner, DatePicker e TimePicker, por fim foi apresentado como
alterar a imagem usada como ícone de lançamento do aplicativo.
9.6 Exercícios
Exercício 1 - Altere o aplicativo Tá na hora para permitir cadastrar as categorias da tarefa, para
isso utilize o menu flutuante no Action Bar e salve as categorias no SQLite.
Apêndice 1 - Instalando e
configurando o ambiente de
desenvolvimento
O objetivo deste apêndice é auxiliar na instalação e configuração de todo ambiente necessário para
o desenvolvimento dos exemplos e exercícios deste livro.
Precisaremos de basicamente dois itens: o Java Developer Kit (JDK) e o Android Studio. A seguir,
será apresentado passo a passo como fazer o download, instalar e configurar cada um deles.
Java Developer Kit
O Java Developer Kit (Kit de Desenvolvimento Java) contém as principais ferramentas para que
possamos programar e compilar os códigos escritos utilizando a linguagem Java, que é uma das
linguagens disponível para desenvolvimento de aplicativos Android.
Para fazer o download do JDK, acesse o site da Oracle⁵ e siga os passos:
1- Escolha a versão mais atual do Java, neste exemplo está no Java SE 9.0.4, e clique no botão
DOWNLOAD do JDK;
⁵http://www.oracle.com/technetwork/java/javase/downloads/index.html
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
171
Figura 1.1 - Fazer download do JDK.
2- Leia os termo de acordo de licença do Java SE e se concordar marque a opção Accept License
Agreement;
3- Clique na versão do download de acordo com o seu sistema operacional, neste exemplo
vou mostrar a instalação para Windows. Neste momento será realizado o download do Java SE
Development Kit.
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
172
Figura 1.2 - Aceitar a licença de uso e fazer download do JDK de acordo com o sistema operacional.
Instalando o JDK no Windows
Para instalar o JDK no Windows, execute o arquivo .exe que foi feito o download e siga os passos
como mostrado a seguir:
1- Na tela Java SE Development Kit … - Setup clique em Next (Próximo);
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
173
Figura 1.3 - Instalar o JDK.
2- A seguir, na tela de Custom Setup, temos a opção de configurar o local do computador que será
instalado o JDK, se quiser alterar o local de instalação clique no botão Change…, caso contrário
clique apenas na opção Next (Próximo) para continuar com a instalação no diretório padrão;
Figura 1.4 - Definir local de instalação do JDK.
3- Agora será solicitado a instalação do Java Runtime Enviroment (JRE) que é o ambiente que
executa as aplicações Java, se você quiser alterar o local de instalação clique no botão Alterar…,
caso contrário clique apenas na opção Próximo >;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
174
Figura 1.5 - Instalar o JRE.
4- Por fim, clique na opção Close (Fechar) para finalizar a instalação do JDK e JRE.
Figura 1.6 - Finalizar a instalação do JDK e JRE.
Agora precisamos configurar as variáveis de ambiente do Java no Windows, para isso siga os passos
como mostrados a seguir: 1- No Windows Explorer, clique com o botão direito do mouse sobre Meu
Computador;
2- No menu escolha a opção Propriedades;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
Figura 1.7 - Acessando as propriedades do computador.
3- Na tela de Sistema clique no link Configurações avançadas do sistema;
Figura 1.8 - Acessando as configurações avançadas do sistema.
4- Na tela de Propriedades do Sistema clique no botão Variáveis de Ambiente…;
175
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
176
Figura 1.9 - Acessando as variáveis de ambiente.
5- Na tela de Variáveis de Ambiente precisamos configurar duas variáveis, para fazer isso, na área
de Variáveis do sistema clique no botão *Novo…;
Figura 1.10 - Criando uma nova variável de ambiente.
6- Defina o nome da variável como JAVA_HOME e o valor da variável o caminho da instalação do
Java no seu computador, clique em OK para criar a variável de ambiente;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
177
Figura 1.11 - Configurando a variável de ambiente JAVA_HOME.
7- Na lista de variáveis do sistema escolha a variável chamada PATH e clique em Editar…, no final
da variável acrescente ;%JAVA_HOME%\bin. Caso não tenha uma variável PATH crie uma nova
variável com o valor %JAVA_HOME%\bin.
Figura 1.12 - Configurando a variável de ambiente PATH.
Essa configuração da variável de ambiente serve para que as demais aplicações encontrem a
instalação do Java e também para que você possa executar uma aplicação Java de qualquer local
do seu computador.
Após isso pode fechar a tela de Variáveis de Ambiente, Propriedades do Sistema e Sistema.
Para testar se a configuração está correta, siga os passos apresentados a seguir:
1- No iniciar do Windows pesquise e abra o programa Prompt de Comando;
2- No Prompt de Comando digite o comando java -version e pressione em ENTER, deverá aparecer
as informações da versão do Java instalado;
3- Digite o comando javac e pressione ENTER aparece uma mensagem informando as possíveis
combinações que podem ser utilizadas no comando javac.
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
178
Figura 1.13 - Testando se o Java foi instalado corretamente.
Pronto, você instalou e configurou o Java no seu computador, agora você já pode começar a
desenvolver suas aplicações Java.
Android Studio
O Android Studio é a IDE que utilizamos para desenvolver os aplicativos para Smartphones, Tablets,
TV e Wear (dispositivos vestíveis como: relógio, pulseira e óculos) Android.
Para fazer download do Android Studio, acesse o site do Android Developer⁶:
1- No site do Android Developers clique no link Obter o Android Studio;
⁶http://developer.android.com
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
Figura 1.14 - Site Android Developers.
2- Na página do Android Studio clique na opção DOWNLOAD ANDROID STUDIO;
179
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
180
Figura 1.15 - Site do Android Studio.
3- Leia os termos e condições de licença do Android Studio, se você estiver de acordo assinale a
opção Li e concordo com todos os termos e condições acima e clique no botão DOWNLOAD
ANDROID STUDIO FOR ….
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
181
Figura 1.16 - Fazer download do Android Studio.
Nesse momento será realizado o download do Android Studio.
Instalando o Android Studio no Windows
Após fazer o download do Android Studio, vamos instalar e configurar o ambiente de desenvolvimento como mostrado a seguir:
1- Na tela Welcome to Android Studio Setup (Bem vindo a configuração do Android Studio)
clique na opção Next (Próximo) para iniciar a instalação;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
182
Figura 1.17 - Instalar o Android Studio.
2- Na tela Choose Componentes (Escolher componentes) é possível escolher quais componentes
do Android serão instalados, assinale as opções: Android SDK - é o kit de desenvolvimento para
Android; e Android Virtual Device - que é o emulador virtual de dispositivos Android; depois clique
no botão Next (Próximo) para continuar a instalação;
Figura 1.18 - Selecionar os componentes que serão instalados.
3- Na tela Configuration Settings (Definições de configuração) é possível escolher o local que o
Android Studio e o SDK serão instalados no seu computador, após escolher o melhor local para você,
clique no botão Next (Próximo) para continuar a instalação;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
183
Figura 1.19 - Definir local de instalação do Android Studio.
4- Na tela Choose Start Menu Folder (Escolher a pasta do menu iniciar) você pode escolher se
deseja criar os atalhos para acessar o Android Studio no Menu Iniciar do Windows, clique no botão
Next (Próximo) para continuar a instalação;
Figura 1.20 - Definir se será criado atalho para acessar o programa Android Studio.
5- Na tela Installation Complete (Instalação completa) após completar a instalação clique no botão
Next (Próximo) para continuar a instalação;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
184
Figura 1.21 - Instalação completa do Android Studio.
6- Na tela Completing Android Studio Setup (Configuração completa do Android Studio)
assinale a opção Start Android Studio (Iniciar Android Studio) e clique em Finish (Terminar)
para finalizar a instalação e inicializar o Android Studio.
Figura 1.22 - Finalizar a instalação e iniciar o Android Studio.
Quando finalizar a instalação o Android Studio será inicializado.
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
185
Figura 1.23 - Finalizar a instalação e iniciar o Android Studio.
Agora temos mais algumas configurações iniciais do Android Studio, como mostrado a seguir:
1- Quando o Android Studio termina de inicializar pela primeira vez aparece a tela de Welcome
(Bem vindo), clique em Next (Próximo) para continuar;
Figura 1.24 - Bem vindo ao Android Studio.
2- Na janela Install Type (Tipo de instalação) deixe selecionado a opção Standard (Padrão) e
clique em Next (Próximo) para continuar;
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
186
Figura 1.25 - Definindo o tipo de instalação.
3- Na janela Select UI Theme (Selecionar o tema de interface de usuário) escolha o tema que
mais te agrada: IntelliJ ou Darcula e clique em Next (Próximo) para continuar;
Figura 1.26 - Selecionando o tema visual.
4- Na janela Verify Settings (Verificar configurações) você pode visualizar tudo que será feito
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
187
download e instalado por padrão, clique em Finish (Finalizar) para continuar;
Figura 1.27 - Verificando as configurações que serão instaladas.
5- Na janela Downloading Components (Fazendo download dos componentes) você pode acompanhar todos os downloads realizados, clique em Finish (Finalizar) para finalizar esta configuração
inicial.
Apêndice 1 - Instalando e configurando o ambiente de desenvolvimento
Figura 1.28 - Finalizar a configuração inicial do Android Studio.
Pronto, a partir daqui o Android Studio está instalado e tem sua configuração inicial.
188
Download