Você está na página 1de 257

Luiz Fernando Duarte Júnior

Criando apps para empresas com


Android

1a edição
Gravataí/RS
Edição do Autor
2017
Copyright © Luiz Fernando Duarte Júnior, 2017
Criando apps para empresas com Android
Luiz Duarte

Duarte, Luiz,
Criando apps para empresas com Android - 1a edição

Luiz Duarte - Gravataí/RS:2017

ISBN 978-65-900538-0-0

1. Programação em Java. 2. Computação, Internet e Mídia Digital.

Reservados todos os direitos.


SUMÁRIO
Sobre o Autor
Antes de Começar
Para quem é este livro
1 Introdução
As Gerações de Celulares
Dispositivos Móveis e seus Nomes
Market Share Mundial
2 Introdução ao Android
A Plataforma
Versões do Android
Como desenvolver
O Mercado de Aplicativos
3 A Ferramenta Android Studio
Instalando
Configurando
Explorando
Testando
4 Interface Gráfica
Layouts
Widgets Comuns
Containers
Dialogs
Menus
5 Entendendo as Activities
Mas o que é uma Activity?
Criando uma Activity
Eventos
Iniciando e Finalizando Activities
6 Acesso à Dados Locais
Arquivos
Banco de Dados
7 Acesso a Dados Remotos
Usando APIs
Criando APIs
API JSON com PHP
API JSON com ASP.NET
API JSON com Java Servlets
API JSON com NodeJS
8 Considerações Finais
Projetos de Apps para empresas
Publicação na Google Play
Política de Privacidade
Seguindo em frente
SOBRE O AUTOR
Luiz Fernando Duarte Júnior é Bacharel em Ciência da Computação pela
Universidade Luterana do Brasil (ULBRA) e Especialista em
Desenvolvimento de Aplicações para Dispositivos Móveis pela Universidade
do Vale do Rio dos Sinos (UNISINOS). Carrega ainda um diploma de
Reparador de Equipamentos Eletrônicos pelo SENAI e duas certificações
Scrum para trabalhar com Métodos Ágeis: Professional Scrum Developer e
Professional Scrum Master.
Atuando na área de TI desde 2006, na maior parte do tempo como
desenvolvedor, é apaixonado por dispositivos móveis desde que teve o
primeiro contato com celulares em 1998, mexendo em um Gradiente Skyway
de sua mãe.
De lá para cá teve oportunidade de lidar com dispositivos móveis em
diferentes oportunidades, incluindo um emprego na área desenvolvendo para
a finada plataforma Palm OS, fora centenas de projetos solitários em J2ME,
até que conheceu Android em 2011.
Foi amor à primeira vista.
Trabalhando com Android desenvolveu diversos projetos para empresas,
incluindo grandes marcas como Embelleze, LG, Ford e Renault, além de
ministrar cursos de Android para alunos do curso superior de várias
universidades.
Um grande entusiasta da plataforma, espera que com esse livro possa ajudar
ainda mais pessoas a criarem seus apps e aumentar a competitividade das
empresas brasileiras.
Além de desenvolvedor de apps, atua como professor do ensino superior no
curso tecnológico de Análise e Desenvolvimento de Sistemas na Faculdade
de Tecnologia de Porto Alegre (RS), é autor do blog
https://www.luiztools.com.br, onde fala sobre empreendedorismo e
desenvolvimento de software, especialmente mobile.
Conheça meus outros livros:

Programação Web com Node.js


Node.js e Microservices
Scrum e Métodos Ágeis: Um Guia Prático
Agile Coaching: Um Guia Prático
Java para Iniciantes
MongoDB para Iniciantes

Conheça meus cursos:

Curso online de Scrum e Métodos Ágeis


Curso online de Node.js e MongoDB
ANTES DE COMEÇAR
Antes de começarmos, é bom você ler esta seção para evitar surpresas e até
para saber se este livro é para você.
Para quem é este livro
Primeiramente, este livro não vai lhe ensinar a programar, ele exige que você
já saiba isso, ao menos em um nível básico (final do segundo semestre da
faculdade de computação, por exemplo). Segundo, este livro exige que você
já tenha conhecimento técnico prévio sobre smartphones, que ao menos saiba
mexer em um e que preferencialmente possua um.
Parto do pressuposto que você é ou já foi um estudante de Técnico em
informática, Ciência da Computação, Sistemas de Informação, Análise e
Desenvolvimento de Sistemas ou algum curso semelhante. Usarei diversos
termos técnicos ao longo do livro que são comumente aprendidos nestes
cursos e que não tenho o intuito de explicar aqui.
O foco deste livro é em ensinar o básico de programação de aplicativos para
smartphones Android focando nas pessoas jurídicas, ou seja, apps para
empresas, que sempre foi o meu foco. Simples assim. Não vou ensinar nada
de iOS ou Windows Phone, apenas Android. Darei foco aos smartphones,
muito embora tudo o que foi visto aqui possa ser aplicado à tablets, sem
problemas.
Novamente, ensinarei o básico. Nenhum tópico avançado será tratado aqui e
nenhuma API muito específica, apenas o que já é nativo da plataforma
Android e completamente focado para empresas, novamente, apps para
empresas pequenas e profissionais liberais. Não faremos um Instagram ou um
Angry Birds, mas muitos conceitos podem ser aplicados para criar um app
profissional de mensagens semelhante ao WhatsApp ou jogos simples no
estilo Quiz.
Novamente, ensinarei a construir aplicativos. Nenhum game será
exemplificado no decorrer do livro e sugiro que busque livros específicos
sobre o assunto se é isso que deseja fazer (em meu blog você encontra outro
livro que fala de games: http://www.luiztools.com.br). Como o foco não é
multimídia ou entretenimento, tão poucos os apps serão muito bonitos,
focando na simplicidade e sobriedade. Deixo o design por conta de quem está
lendo, enquanto construímos as interfaces de maneira simples e funcional.
Ao término deste livro você estará apto a construir aplicativos simples para
smartphones Android, com componentes de interface, transições de telas,
layouts personalizados, integração com APIs externas, banco de dados local e
podendo testar tudo isso no seu próprio smartphone ou no simulador Android
que vem junto com a ferramenta de desenvolvimento que utilizaremos. Além
disso, terá uma noção abrangente do cenário mobile atual e do mercado em
que está se inserindo.

Atenção: se você não sabe o básico de Java, leia esse livro primeiro Java
para Iniciantes, do mesmo autor.
1 INTRODUÇÃO
Estamos vivendo uma Revolução Mobile. Ninguém tem dúvidas disso. A
pergunta é: você está preparado para surfar esta onda?
A teoria dos celulares é de 1947, mas somente em 1973 que a Motorola se
tornou a pioneira desse mercado ao construir o primeiro protótipo funcional
de um telefone celular que, dizem as histórias, foi utilizado pelo diretor da
Motorola na época para realizar uma ligação para seu rival da AT&T
avisando que ele havia ganho a corrida tecnológica pelo primeiro celular.
Entretanto, ainda levou 10 anos para os primeiros aparelhos celulares
chegarem ao mercado com tamanho em torno de 30cm e pesando 1kg. O
preço? Módicos U$4.000 que tornavam este um item de luxo tanto quanto os
carros que eram equipados com estes aparelhos, como Rolls Royces e
Bentleys. Sim, ou você acha que as pessoas carregavam aparelhos de 1kg no
bolso?
No início os celulares eram usados somente para realizar ligações telefônicas.
Somente em 1993 que surgiu o SMS, o sistema de envio de mensagens que
até o surgimento recente do WhatsApp dominava a mensageria telefônica
mundial, pondo bilhões no bolso das operadoras.
Ainda nesse ano não tão longe do início da década de 90, surgiram os
primeiros celulares com funções PDA, como agenda de contatos, calendário
de compromissos, alarmes, etc quando a IBM passou a investir mais forte
neste mercado. Os celulares estavam se tornando cada vez mais úteis.
Em 1996, este artefato tecnológico começou a ganhar uma conotação de
status. Não exatamente neste ano que ter um celular se tornou motivo de
status, mas foi em 1996 que a Motorola novamente foi pioneira lançando o
famoso Motorola StarTac, com design inspirado na nave espacial da série
Star Trek. Sim, design.
Estávamos entrando em uma era onde os celulares eram quase peças do
vestuário como os relógios. Na verdade, mais à frente os celulares acabaram
substituindo parcialmente os relógios de pulso, e de certa forma tem de
combinar com a personalidade e vestes do seu dono, não é mesmo?
Avançando no tempo, em 2001, a Kyocera, uma fabricante não tão famosa no
Brasil, lançou o primeiro smartphone do mercado. Ok, não era tão “smart”
assim, não tinha tantos recursos quanto os atuais, mas tinha um sistema
operacional de verdade e não um firmware embarcado em um hardware. Ele
podia ser formatado, podia ter aplicativos instalados. Não tínhamos à essa
altura uma loja de aplicativos ou sequer a liberdade de desenvolvê-los, mas
abriu as portas para as inovações que surgiram mais tarde.
Selfie tem sido a palavra mais usada atualmente nas redes sociais. Mas você
parou para pensar o que gerou essa onda de narcisismo digital?
Foi em 2002 que a Sanyo, outra fabricante de celulares pouco conhecida aqui
no ocidente teve a ideia de lançar um celular que viesse com uma câmera
digital embutida. Claro, era uma câmera VGA de 0.3MP, mas que gerou uma
revolução na fotografia digital, permitindo que fabricantes como a Nokia se
tornasse a maior vendedora de câmeras digitais do mundo no ano seguinte,
devido ao sucesso de seus smartphones, principalmente a linha N Series.
Mas e a Internet? Hoje todos vivemos conectados à ela com nossos
smartphones, certo?
Apenas em 2003 que o primeiro smartphone se conectou à web real, o HTC
Danger. Além disso ele foi o primeiro smartphone com comandos por voz
algo que hoje é muito popular com o Siri da Apple e o Google Now do
Google.
E a revolução continuou em 2003 com a Nokia lançando o primeiro
smartphone focado em games, o Nokia N-Gage, que inclusive tinha um
formato peculiar de console portátil, plataforma de games e até algum tipo de
rede social para os jogadores. Nokia Arena ou algo assim.
Foi em 2004 que a Motorola voltou a inovar com seus celulares-design,
lançando o famosíssimo Motorola Razr, mais conhecido como V3, que
durante 3 anos consecutivos se manteve na lista dos celulares mais vendidos
no mundo inteiro, e aqui no Brasil não foi exceção.
Ter um V3 era chamar a atenção toda vez que tinha de tirá-lo do bolso para
tirar uma foto, atender uma ligação ou...praticamente apenas isso. O V3 não
era um celular de ponta mesmo na sua época, seu apelo era mais social, com
design ultra fino, teclado no melhor estilo Tron e duas câmeras de diferentes
qualidades dependendo da versão.
E quantas versões, hein! Tinha V3 padrão (prata), Black, Pink, Dolce &
Gabanna (dourado) e muitas outras, sendo que o hardware variava pouco e o
design mudava basicamente a cor. Curioso não?!
Foi em 2005 que a Motorola lançou outro marco da indústria com a série
Rokr que eram celulares focados em ser os melhores tocadores de música do
mercado. Com enorme capacidade de armazenamento, um bom player e
caixas estéreo de alta potência, os Rokrs eram junto com os Sony Walkman
os melhores celulares para se escutar música na época, fazendo a alegria de
quem tinha grana para comprá-los e escutar no caminho para a faculdade ou
trabalho.
Foi praticamente o início da morte dos MP3 Players, que duraram no
mercado mais alguns anos até se extinguirem completamente. Afinal, para
quê carregar um celular e um MP3 no bolso, se um bom Rokr resolve os dois
problemas?
Em 2007 temos o maior marco da indústria mobile moderna. Algo como a
Revolução Francesa dos dispositivos móveis, uma vez que toda uma indústria
nova foi criada neste ano, a indústria dos apps.
Foi com a Apple, que em seu princípio era uma empresa de computadores,
que virou uma empresa de tocadores de música com iPod, que criou o mais
incrível de todos os smartphones até então, o iPhone.
Mas o mais incrível de 2007 não é o lançamento do iPhone em si, que era um
excelente telefone e trazia um conceito completamente novo , com uma
experiência completamente focada no touchscreen (que não era uma
tecnologia nova, diga-se de passagem), mas no ecossistema que a Apple criou
junto ao iTunes e a App store. Agora, qualquer desenvolvedor em qualquer
lugar do mundo podia escrever seu próprio aplicativo e distribuí-lo em um
marketplace, alcançando clientes do mundo inteiro.
O desenvolvimento de apps, até então centralizado nas mãos das empresas
credenciadas junto às fabricantes, agora estava aberto a qualquer
desenvolvedor com um computador na mão e uma ideia na cabeça. Se você
hoje está pensando em desenvolver um app para colocar na Apple Store ou na
Google Play, agradeça à Apple por ter criado este modelo de distribuição de
apps que forçou toda a indústria a se reinventar.
Não demorou muito até o Google se manifestar e querer entrar nesta briga,
lançando junto com a HTC o primeiro smartphone Android, seu novíssimo
sistema operacional, em 2008, o HTC Dream.
Enquanto que o mercado esquentou absurdamente em 2010 com a Samsung
se tornando a maior fabricante de celulares do mundo, com a Nokia perdendo
a liderança com seu defasado sistema Symbian, com a BlackBerry entrando
em colapso e perdendo seu lugar até mesmo para a Microsoft, a Apple não se
acomodava e antes do lendário Steve Jobs dizer adeus à empresa (e ao
mundo), o iPad foi lançado, iniciando todas em uma nova corrida por tablets
de todos os tipos, tamanhos e fabricantes. Os tablets não eram algo novo,
fizeram parte de um passado não tão distante, mas um passado desconectado
que fez com que não fossem bem sucedidos. Hoje, um tablet conectado à
Internet é tão útil quanto um notebook ou computador para 90% das pessoas,
e muito mais conveniente.
A história não termina aqui, mas acho que você já entendeu, não é mesmo?
As Gerações de Celulares
Na década de 80 quando os celulares começaram a se popularizar tivemos a
chamada 1G, onde celular era coisa para se fazer e receber ligações e
usávamos o padrão TDMA de comunicação que durou por décadas.
Na década de 90 tivemos a segunda geração, ou 2G, com novas redes digitais
como a CDMA e mais tarde com a GSM e a Internet móvel Wap.
No início dos anos 2000, com a profusão do GSM em larga escala e
praticamente morte do CDMA, padrão anterior, tivemos uma geração
intermediária e não oficial chamada 2.5G, caracterizada pelos celulares com
câmeras digitais entre 0.3MP e 2MP, Internet mais rápida (padrões GPRS e
EDGE) e MP3 players embutidos.
Por volta de 2009 começamos a galgar um aumento na velocidade da Internet
móvel com a terceira geração, ou 3G, caracterizada pela banda-larga móvel e
vídeo-chamada, com investimento pesado de players como Claro e Vivo e
mais tarde as demais operadoras, todas tentando mudar o hábito de consumo
de Internet no país, até então centralizado nas operadoras de telefonia fixa.
Tem alguns poucos anos que vemos o movimento 4G no Brasil. Com padrão
pouco definido e velocidades que deveriam estar cobertas pela 3G.
Infelizmente a quarta geração é muito mais antiga em países desenvolvidos,
sendo que atualmente estes mesmos países já possuem velocidades de
conexão de até 100Mbps em movimento e 5Gbps em repouso, o que não
temos nem mesmo em nossos computadores domésticos na época de escrita
deste livro.
Dispositivos Móveis e seus Nomes
Muitos são os nomes dados aos dispositivos móveis. Você conhece todos
eles?
Handheld ou Handset: nome dado a qualquer dispositivo móvel, com ou
sem a função de celular.
PDA ou Personal Digital Assistant: um Assistente Digital Pessoal não é um
celular, mas um celular pode ter funções de PDA. Um PDA é como os
antigos PalmTops e agendas eletrônicas de antigamente, que caíram em
desuso tem alguns anos.
Smartphone: um telefone inteligente é um telefone celular com um sistema
operacional de verdade. Ou seja, o hardware é construído e depois é instalado
um sistema operacional, que pode ser reinstalado outras vezes caso
necessário, bem como aplicativos.
Pocket PC: foi um celular com funções de PDA criado pela Microsoft, que
rodava o famigerado Windows CE, sistema operacional móvel criado pela
empresa de Redmond que não vingou por não ser tão móvel assim.
Tablet PC: ou simplesmente tablet é o nome dado ao meio termo entre os
smartphones e os notebooks, como pranchetas digitais com telas sensíveis ao
toque ou à canetas Stylus.
Market Share Mundial
Os números de 2017 mostram uma supremacia do sistema operacional
Android sobre todos os outros. Se esse livro tivesse sido escrito na década de
90 com revisões a cada 10 anos, mostraria o quanto esse mercado mudou
com o passar dos anos, com o surgimento e desaparecimento de sistemas
operacionais e fabricantes.

Esses números são de uma pesquisa da IDC e mostra que o Android reina no
mundo inteiro, com variações em cada continente, mas sempre com alguma
folga, como nos EUA onde tem 60% do mercado e na China, onde tem 90%.
2 INTRODUÇÃO AO ANDROID
Você sabia que não foi o Google que criou o Android?
O sistema operacional Android foi criado em 2005 por uma startup chamada
Android Inc. Que foi comprada pela empresa de Mountain View e se tornou a
equipe que criou este fantástico SO.
Apenas em outubro de 2008 que tivemos o lançamento oficial do Android no
mercado com o primeiro smartphone Android, o HTC Dream.
Mais tarde, em 2010 tivemos o lançamento do Samsung Galaxy Tab, o
primeiro tablet com Android.
Talvez a maior inovação trazida pelo Android não tenha sido suas APIs,
suporte a múltiplos hardwares, não somente celulares, mas sim o fato de ser
uma plataforma aberta, com todos seu código fonte disponível para download
e customização, inclusive para fins comerciais como bem tem feito a
Samsung nos últimos anos, que hoje fatura mais com o Android do que o
próprio Google.
A Plataforma
O Android é um sistema operacional que compartilha o mesmo kernel do
Linux, escrito em C e C++ com um pouco de linguagem de montagem
Assembly.
Ao contrário do que se pensa o Android não é escrito em Java, o que na
verdade o faria muito lento se fosse verdade.
Outro mito relacionado ao Android é de que ele é de propriedade do Google.
Embora o Google seja o principal mantenedor da plataforma, o Android é
propriedade da Open Handset Alliance, um consórcio de empresas criado
pelo Google e com participantes das principais fabricantes de celulares do
mundo como Motorola, Samsung e LG.
Curiosamente, em 2012 o Google comprou a divisão de mobilidade da
Motorola, seja para aumentar seu poder sobre o mercado ou apenas pensando
em capitalizar melhor a plataforma.
Além do sistema operacional, a plataforma engloba uma camada de aplicação
ou middleware, em Java, onde a maioria dos aplicativos reside, conforme
mostra a imagem abaixo.
Nesta camada o Android já entrega alguns apps genéricos como um browser
webkit, um alarme, uma agenda de contatos, uma calculadora e mais alguns.
A plataforma Android oferece suporte nativo à biblioteca gráfica OpenGL ES
2D e 3D para renderização de apps e games, além de suporte nativo ao banco
de dados SQLite.
Versões do Android
Desde seu lançamento em versões Alfa e Beta, o Android teve diversas
versões, sempre com nomes de sobremesas e em ordem alfabética,
começando na letra C uma vez que já tivemos a A (Alfa) e B (Beta).
O número de API entre parênteses é o número da biblioteca de
desenvolvimento, que usaremos mais tarde.
● Versão 1.5 Cupcake (API 3)
● Versão 1.6 Donut (API 4)
● Versão 2.0 a 2.1 Eclair (API 7)
● Versão 2.2 Frozen Yogurt (ou FroYo, API 8)
● Versão 2.3 Ginger Bread (API 9 e 10)
● Versão 3.0 Honeycomb (API 11 a 13)
● Versão 4.0 Ice-cream Sandwich (API 14 e 15)
● Versão 4.1 à 4.3 Jellybean (APIs 16, 17 e 18)
● Versão 4.4 Kit Kat (API 19 e 20)
● Versão 5.0 Lollipop (API 21 e 22)
● Versão 6.0 Marshmallow (API 23)
● Versão 7.0 Nougat (API 24 e 25)
● Versão 8.0 Oreo (API 26)
Até a versão 2.3 o Android era praticamente 100% focado para smartphones
e o uso destas versões do Android em tablets era sofrível, embora existente
até os dias atuais em dispositivos de segunda linha do mercado chinês.
Somente na versão 3.0 que o Android passou a atender de verdade o mercado
de tablets com a versão Honeycomb, que era exclusiva para esse tipo de
dispositivo. No entanto, essa nova versão trouxe à tona outro problema da
plataforma: a fragmentação.
No mundo inteiro diversos fabricantes lançaram celulares e tablets com
versões diferentes do Android, o que gera problemas até hoje para os
usuários, vendedores, fabricantes e nós, desenvolvedores.
A OHA e principalmente o Google tentou consertar essa fragmentação com a
versão 4.0, a Ice-cream Sandwich que visava unificar as vantagens da versão
2.3.3 com o suporte à tablets da versão 3.0 em uma versão completamente
reescrita e reestilizada.
Mais recentemente em 2014, o Google deu um novo empurrão em sua
plataforma anunciando no Google IO, seu evento global para os
desenvolvedores, que estaria lançando versões do Android para automóveis e
wearables, ou seja, em breve teremos Android em nossos carros, óculos,
relógios e até mesmo roupas!
E a empresa californiana não está para brincadeira, junto com grandes
montadoras como a Hyundai, o Google montou em analogia à OHA a Open
Automotive Alliance, com o intuito de definir os padrões e escrever o futuro
dos computadores de bordo e centrais multimídias dos carros usando a
plataforma Android.
Não obstante, o próprio Google Glass, óculos de realidade aumentada do
Google que ainda engatinha nas vendas, usa a plataforma Android.
E por fim, o suporte à Smart TVs e a aparelhos da linha branca como as
geladeiras inteligentes da Brastemp, mostram que o Android veio para ficar.
Como desenvolver
A dita plataforma Android não é apenas um sistema operacional. O Google
não nos presenteou apenas com um grande e gratuito sistema operacional
para smartphones e tablets, mas com todo um set de recursos para
desenvolver para ele.
Para desenvolver para Android você precisa ter instalado em sua máquina o
JDK (Java Development Kit) e o Android SDK (Software Development Kit),
que está disponível publicamente aos desenvolvedores desde setembro de
2008.
Isto considerando o desenvolvimento nativo tradicional, com a linguagem
Java. Neste post em meu blog falo de outras possibilidades de
desenvolvimento para Android: http://www.luiztools.com.br/post/conheca-
os-frameworks-de-desenvolvimento-mobile/
Como ambientes de desenvolvimento pode-se utilizar Eclipse, Netbeans ou
IntelliJ, entre outras.
O SDK oficial engloba o ADT ou Android Development Toolkit, um kit de
desenvolvimento que pode ser instalado em IDEs compatíveis que fornece
recursos de compilação e de conexão, como o ADB, a Android Debug
Bridge, e de simulação, como o AVD ou Android Virtual Device. Mas nem
só de plugins e componentes vive o SDK, ele possui todas as bibliotecas e
APIs para manipular os apps nativos da plataforma e os recursos de hardware
do dispositivo, como GPS, acelerômetros, tela sensível ao toque, redes de
dados, etc.
Mas o desenvolvimento para Android, que é um sistema tradicional escrito
em C, não está restrito a esta linguagem. Pode-se desenvolver em Android
com a plataforma .NET, com HTML+CSS+JS, com a própria linguagem C e
C++ (usando o NDK, o Native Development Kit), com a linguagem brasileira
Lua e com muitas outras, com diferentes níveis de performance,
compatibilidade e sets de recursos.
Apesar dos aplicativos Android em sua maioria serem escritos em Java, a
máquina virtual Java (JVM) que roda nos dispositivos Android não é a
tradicional que roda em desktops.
Chamada de Dalvik, é uma máquina virtual reduzida, com seu próprio set de
instruções e que não lê os mesmos bytecodes do Java desktop, ou seja, não há
compatibilidade entre os binários de ambas plataformas, e mesmo através de
recompilação, nem todas bibliotecas Java tradicionais funcionam no Android.
Tenha isso em mente, principalmente se quiser converter alguma aplicação
desktop para mobile.
Se quiser mais sobre a Dalvik, recomendo a leitura deste post em meu blog:
http://www.luiztools.com.br/post/tudo-sobre-maquina-virtual-dalvik-do-
android/
O Mercado de Aplicativos
O mercado de apps irá movimentar U$77 bilhões em 2017 segundo a
Gartner, devido a um volume de 268 bilhões de downloads de apps. São 2
milhões de apps para download na App Store e mais 2.2 milhões na Google
Play.
A empregabilidade de desenvolvedores de aplicativos está entre as mais altas
do mundo, mesmo dentro de um setor como a TI que já impressiona pelos
números. Nos EUA os salários beiram os U$100/h e mesmo dentro do Brasil
não é raro encontrar empresas pagando salários de R$5.000 a R$12.000 para
bons desenvolvedores de aplicativos, conforme mostrado pelo site Glassdoor:
https://www.glassdoor.com/Salaries/brazil-android-developer-salary-
SRCH_IL.0,6_IN36_KO7,24.htm
O mais impressionante de tudo isso é que para entrar nesse mercado não é
preciso muito.
Você pode desenvolver para Android com qualquer plataforma e com uma
infinidade de ferramentas gratuitas. Ao contrário do iOS, você pode distribuir
e vender seus aplicativos livremente sem pagar royalties a ninguém. Caso
queira publicar na Google Play existe uma taxa única de U$25 contra os
U$99 anuais da Apple.
Ou seja, todo o investimento é o de um computador e do seu tempo. Claro, se
você está lendo isso é porque comprou este livro também, então teve mais
algum investimento ☺.
Devido a isso de vez em quando aparecem grandes cases de sucesso
surpreendentes, como caso de Robert Nay que aos 14 anos, estudante da 8a
série da escola elementar, criou o game Bubble Ball que com 9 milhões de
downloads desbancou o trono de Angry Birds à época.
Tudo isso com um livro de programação mobile que encontrou na biblioteca
de sua escola.
Mas que tipos de aplicativos movem este mercado?
Muitas são as opções de aplicativos para desenvolver, mas algumas
categorias lideram em números:
Consumo de API
Basicamente um app de consumo de conteúdo é um app que não possui
conteúdo próprio, que se conecta a alguma API ou feed para carregar seu
conteúdo, como os apps de redes sociais, leitores de feed RSS, revistas
digitais, mobile bankings, entre outros.
Utilitários
Um app utilitário é um app que lhe ajuda a realizar outras tarefas como ver o
saldo da sua conta bancária, escrever e-mails, ou os discos virtuais.
Entram aqui também os diversos apps de fotografia e compartilhamento de
imagem e os apps mensageiros e de localização.
Advergames
Advergames são os jogos associados a grandes marcas de produtos, como os
jogos da Pepsi, Toddynho, Doritos, Axe, Boticário e Rexona, só para citar
alguns.
As grandes marcas estão cada vez mais investindo em jogos para engajar seu
público de uma maneira mais lúdica e alguns projetos de jogos que chegam
nas agências digitais e estúdios desenvolvimento chegam na casa dos R$100
mil.
Casual Games
Jogos casuais existem há décadas, divertindo seus jogadores nas horas livres,
nas filas dos bancos, no ônibus e nas longas viagens.
Um jogo casual é aquele que é simples de jogar mas extremamente viciante.
Geralmente um jogo casual tem muitos níveis com pouca variação, para lhe
manter o maior tempo possível jogando, mas sem uma história geralmente.
Em celulares, onde a jogabilidade é limitada, os jogos casuais reinam
absoluto.
Títulos como Clash Royale, Bejeweled e Candy Crush são exemplos de jogos
casuais sendo que a Supercell, empresa criadora do Clash Royale, vale
bilhões no mercado de capitais.
Adaptações de Grandes Games
Grandes empresas de games como a Electronic Arts (EA) atualmente
investem mais nas plataformas móveis do que nos consoles e PCs.
Em parte isso se deve ao baixo índice de pirataria dos jogos mobile e ao custo
de produção menor que o dos jogos tradicionais. Seja lá o motivo, as
adaptações de grandes games como FIFA e Pro Evolution Soccer para
celulares tem rendido milhões às contas de suas produtoras, só para citar dois
exemplos.
Futilidades
Nesta categoria encontram-se todos apps que não possuem uma utilidade
prática mas que ainda assim fazem enorme sucesso.
Exemplos incluem um ventilador que não faz vento, um app que zumbifica as
fotos dos seus amigos, flatulência digital e por aí vai.
Ganhando dinheiro com apps
Diversos são os valores destes aplicativos e 80% de todo o faturamento do
mercado de apps mobile vem de games gratuitos, que mais tarde vendem
bens dentro do jogo ou usam de publicidade. Os demais games possuem
valores entre U$0,99 e U$12, sendo que a imensa maioria se encontra na
extremidade de menor valor.
Seja qual for o gênero ou preço, o fato é que o mercado de apps está
bombando.
Empresas como a Evernote, possuem 100 milhões de usuário que geram mais
de U$150 milhões ao ano.
O Waze, popular app de mapas e rotas possui mais de 40 milhões de usuário
e foi comprado pelo Google por U$1,3 bilhões.
A Supercell, criadora de sucessos como Hay Day e Clash of Clans teve 51%
de suas ações compradas por U$1,5 bilhões por um banco japonês.
Outra notória compra foi a do Instagram, que com 260 milhões de usuários
foi comprado pelo Facebook, no valor de U$1 bilhão entre dinheiro e ações
da própria empresa.
A Google Play possui atualmente mais de 2.2 milhões de apps e por dia são
ativados 850 mil dispositivos Android no mundo.
O que você está esperando para ter o seu lugar ao sol?
Se quiser saber mais sobre como ganhar dinheiro com apps, recomendo este
post do meu blog http://www.luiztools.com.br/post/fiz-um-app-e-agora-
como-ganho-dinheiro/
3 A FERRAMENTA ANDROID STUDIO
Existem diversas ferramentas possíveis de se usar para desenvolver
aplicativos para Android.
Nenhuma delas supera a criatividade e competência de um bom
desenvolvedor, mas todas ajudam a aumentar sua produtividade e lhe
permitem criar apps cada vez mais profissionais.
Escolher uma boa ferramenta é uma boa maneira de começar na frente no
desenvolvimento de apps, uma vez que uma má ferramenta pode lhe atrasar
em demasia ou mesmo fazer com que perca tempo com configurações ou
mesmo falhas de software ao invés de apenas programar.
Recomendo e uso no desenvolvimento deste livro a ferramenta oficial do
Google, chamada de Android Studio, uma IDE construída sobre o IntelliJ,
outra IDE de código aberto assim como o famoso Eclipse.
O Android Studio encontra-se, na época de escrita deste livro, na sua versão
2.1, e tem se mostrado bem estável e com atualizações mensais, o que é uma
boa vantagem, mostrando que o Google realmente está investindo tempo e
dinheiro no seu desenvolvimento.
Usaremos esta ferramenta durante os estudos do livro e pode baixá-la neste
link: https://developer.android.com/studio/index.html .
Caso deseje usar outra ferramenta, em meu blog ensino como usar:
● Eclipse: http://www.luiztools.com.br/post/como-programar-apps-
android-no-eclipse/
● NetBeans: http://www.luiztools.com.br/post/como-programar-apps-
android-no-netbeans/
Instalando
Antes de instalar o Android Studio você irá precisar ter o JDK instalado em
sua máquina, que pode ser baixado no site oficial da Oracle (na época de
escrita deste livro a versão mais recente é a Java 8):
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-
2133151.html
No site da Oracle existirão uma dezena de versões do JDK para baixar,
procure o seu sistema operacional na lista e baixe a versão mais recente.
Baixe e instale o JDK apenas avançando durante o instalador, para somente
depois mandar instalar o Android Studio. Caso você não instale nessa ordem,
o Android Studio não irá encontrar sozinho o JDK e exigirá que você
configure seu sistema operacional manualmente, definindo uma variável de
ambiente JAVA_HOME para a pasta do seu JDK.
Assim que estiver com o JDK instalado, baixe a última versão do Android
Studio (que na época em que escrevo este livro é a 2.2) no site oficial:
https://developer.android.com/studio/index.html
Baixe e instale o Android Studio apenas avançando durante o instalador.
Após a instalação, siga em frente executando pela primeira vez o Android
Studio, seja pelo menu Inicializar do Windows, pela pasta de aplicativos no
Mac OSX ou como quer que chamem o “Inicializar do Linux”.
Atenção: certifique-se de instalar o Android Studio em um caminho que não
contenha espaços em branco ou acentos, para evitar problemas de
compatibilidade mais tarde.
Configurando
Ao abrir o Android Studio você deve visualizar a seguinte tela, logo após a
splash screen. Clique na opção Configure (no rodapé à direita) e depois em
SDK Manager.

No SDK Manager você gerencia a versão das ferramentas do Android SDK


que está usando com o Android Studio, bem como quais versões de Android
você tem instaladas na sua máquina.
Por padrão junto com a instalação já vem com a versão mais recente instalada
e a menos que vá desenvolver para alguma versão específica, essa será o
suficiente para os exemplos deste livro.
Caso queira baixar alguma versão específica, ou tenha de atualizar alguma
coisa no SDK, marque as opções que deseja e clique no botão Ok que tudo
será baixado e talvez você tenha que reinicializar o Android Studio para que
tudo volte a funcionar normalmente.
Mais tarde, já com a IDE aberta no modo de edição de código, caso deseje
abrir o SDK Manager, você pode clicar no ícone do mesmo que fica no lado
direito da barra de ferramentas.

Evite a tentação de sair marcando todas opções e prefira as versões mais


genéricas do Android para trabalhar, como a versão 4.0, que atende a boa
parte das exigências.
Você deve estar se perguntando: “Mas e as demais versões?”.
O Android tem uma característica peculiar que se você está desenvolvendo
um software para a versão 4 da plataforma, todas as versões mais recentes
conseguirão usar este app, mas o contrário não é válido.
Então não seria uma boa desenvolver sempre para a 1.5?
NÃO!
Isso porque a versão de SDK que escolhemos, também chamada de
Minimum SDK restringe as bibliotecas a que temos acesso. Por exemplo, se
queremos usar algum recurso de comunicação NFC, só encontraremos API
para isso na versão 4.0 do Android.
Explorando
O Android Studio é uma IDE bem completa.
Possui um editor de código com code complete (ele vai te dando dicas
conforme vai escrevendo as palavras) e highlight syntax (ele colore as
palavras reservadas conforme suas funções, bem como comentários).
Possui ferramentas de depuração muito boas e já vem 100% integrado com o
Android SDK, incluindo alguns botões exclusivos e projetos para os apps que
podemos querer criar.
Para explorar a IDE melhor vamos criar nosso primeiro projeto com ela. Para
isso, clique em Start a New Android Project na tela inicial, ou se já estiver
dentro da ferramenta, vá no menu File > New Project.
Mantenha as informações iniciais que indicam o nome da aplicação (My
Application), o domínio da empresa e a pasta do projeto (dentro da pasta do
seu usuário) e clique em Next.
Na tela que se abrir, a Target Android Devices, selecione a opção “Phones e
Tablets”, informe versão 4.0 do Android na opção Minimum SDK, ou a mais
próxima que tiver disso. O Android Studio vai lhe informar a porcentagem de
dispositivos Android que seu app irá funcionar.

Next e poderá escolher qual modelo de app irá usar para criar o seu. Escolha
a opção Empty Activity, que explicaremos do que se trata mais tarde.
Atenção: se você selecionar uma versão de Android que ainda não tenha
baixado para sua máquina, o Android Studio irá começar o download por
conta própria agora mesmo, o que pode demorar um pouco.

Avance e chegará à última tela, que lhe pede o nome da Activity (nem
sabemos o que é isso ainda), o nome do Layout e o Título da Activity. Deixe
tudo como está e mande encerrar clicando no botão de Finish.
Agora sim podemos explorar a ferramenta!
Atenção: o Android Studio é uma ferramenta bem pesada e com uso
constante de Internet. É praticamente impossível usá-lo completamente sem
estar conectado e você verá que muitas vezes ele poderá estar um pouco
lento, principalmente nesta primeira etapa de criação e configuração do
projeto e mais para frente em etapas de compilação. Ter um SSD ajuda muito
nestas horas pois o IO de disco é intenso.
Project
Na imagem acima temos a seção Project, que lista toda a estrutura de pastas e
arquivos do projeto.
Mais tarde iremos estudar exatamente para que servem cada uma destas
pastas e arquivos.
Por ora, apenas note que os fontes do nosso aplicativo ficam em app/java/ e
por fim o pacote das suas classes Java, onde estão a lógica do seu app. No
meu caso é o pacote com o nome de com.example.luizfduartejr.myapplication
Atenção: Se você não estiver vendo algo muito parecido com isso na sua
ferramenta pode estar com uma configuração de visualização do projeto
diferente da minha. Note um botão “Android” logo acima da pasta app,
clicando nele você pode mudar a forma de ver e gerenciar o projeto.
O Menu View
Caso perca esta seção (Project) ou outra qualquer, você pode facilmente
exibi-las novamente usando o menu View > Tool Windows e escolhendo a
janela ou seção que “perdeu” durante o desenvolvimento.
É no menu View que temos também dois recursos muito interessantes para
pessoas como eu, que tem de dar cursos de Android: Enter Presentation Mode
e Enter Full Screen.
A primeira opção otimiza toda área de trabalho do Android Studio para
exibição em um telão, com foco no editor de código em si. A segunda opção
maximiza a área de trabalho e é indicado para trabalhar em projetos com
grande quantidade de código Java a ser analisado, e até mesmo para aumentar
o foco do desenvolvedor no projeto sem ser distraído com outras janelas.
Qualquer uma destas opções pode ser revertida acessando o mesmo menu
View novamente e clicando em Exit Presentation Mode ou Exit Full Screen,
respectivamente.
Editor de Código
No centro da IDE, desde que uma classe Java esteja aberta (como
MyActivity.java), você verá o editor de código, organizado em abas para
melhorar a navegabilidade entre os documentos que estão sendo editados no
momento, com a possibilidade de fechar quaisquer documentos que não estão
sendo usados no botão ‘x’ no canto direito de cada aba.
Cada um desses documentos pode ser aberto através da seção Project à
esquerda, que foi vista no tópico anterior. Por ora vamos nos ater às
funcionalidades e não ao código que foi gerado automaticamente durante a
criação do projeto com o modelo Empty Activity.
Código 1: disponível em luiztools.com.br/livro-android-fontes

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

O editor de código possui recursos de autocomplete e de highlight syntax, o


que aumenta e muito a produtividade e legibilidade do código,
respectivamente.
A IDE também irá lhe avisar em tempo real sobre erros de codificação
grifando as palavras em vermelho, além de dicas de melhorias no seu código
grifando as palavras em amarelo.
Na margem esquerda do editor temos algumas setas que permitem ocultar ou
exibir pequenos trechos do código, geralmente delimitados por chaves,
indicando escopos isolados (ou seja, grupos de comandos com um objetivo
comum).
Outra característica do editor é que quando digitamos o nome de uma classe
ainda não referenciada, ele pode sugerir que você crie a classe
automaticamente ou que importe uma classe já existe com esse nome.
Editor de Layout
Com um arquivo de layout aberto, como activity_main.xml que foi gerado e
deve estar em outra aba do editor, o editor de código é substituído pelo Editor
de Layout no centro da IDE, conforme mostra a figura abaixo:
Neste Editor temos duas formas de visualização, que podem ser acessadas
pelas abas no rodapé do editor: “Design” e “Text”. Clique em cada uma delas
e veja a diferença.

Com o modo Text selecionado, vemos basicamente o conteúdo do arquivo


XML em si, permitindo que toda a interface seja construída apenas através do
uso correto das tags XML permitidas e interpretadas pelo Android.
É dessa maneira que as interfaces gráficas são construídas em Android, o
interpretador da máquina virtual Dalvik (a JVM reduzida do Android) lê o
arquivo XML e sabe exatamente o quê, onde e como devem ser renderizados
cada um dos elementos da interface.

Note que mesmo com a aba Text selecionada, ainda temos uma ferramenta
visual à direita para nos ajudar a entender o que estamos criando.
Quando alteramos o texto de algum controle na esquerda, o mesmo é
automaticamente exibido no simulador à esquerda.
Não obstante, o editor de código XML é muito bom e conta também com
recursos como code complete (vai dando sugestões enquanto você escreve) e
highlight syntax (colore as palavras de acordo com sua função), tornando
muito produtiva a tarefa de construção de interfaces em modo texto.
Ainda assim, se você preferir, pode utilizar a aba Design para construir sua
interface visualmente, arrastando componentes da Palette, que fica à esquerda
do simulador.
A cada componente arrastado, um trecho novo de código é adicionado em
background ao arquivo XML de interface, ou seja, no fundo, só existe uma
forma de construir o layout, sendo que a Palette é apenas um recurso gráfico
para facilitar sua vida.
O mais comum é que seja utilizado uma mescla das duas abordagens,
utilizando a Palette para criar o componente na interface e usando a aba Text
para configurar o layout e suas propriedades e às vezes até para copiar e colar
alguns trechos.
Falando em propriedades, cada um dos atributos do nó XML do arquivo de
layout é considerado uma propriedade do componente.
Além disso, quando selecionamos um componente no modo de edição visual,
na direita aparece uma seção Properties, com as propriedades passíveis de
configuração daquele componente, conforme mostra a imagem abaixo,
quando selecione com o mouse um TextView (rótulo de texto):
Estas propriedades tanto podem ser manipuladas visualmente pela seção
Properties quanto em modo texto. Note que as mesmas propriedades
aparecem nesse trecho de código do arquivo XML de layout:
Código 2: disponível em luiztools.com.br/livro-android-fontes

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world!" />

E com isso terminamos nossa exploração inicial da interface da IDE Android


Studio. A seguir, testaremos esse app de teste, rodando nossa primeira
simulação!
Testando
Para testar o Android Studio vamos criar nosso primeiro app, que na verdade
será apenas um app que exibirá a frase Olá Mundo na tela do simulador do
smartphone.
Seguindo os passos da seção anterior, teremos um app de Hello World já
pronto e basta configurarmos um dispositivo virtual de testes, os chamados
AVDs (Android Virtual Devices).
Para isso, com o Android Studio aberto, clique no ícone do AVD Manager,
localizado na Toolbar.

Isso irá abrir a janela AVD Manager, como mostrado abaixo. O AVD
Manager serve para gerenciar as máquinas virtuais Android que usaremos
para a maioria dos testes e exemplos práticos deste livro.
Obviamente nada é melhor do que testar seus aplicativos em um dispositivo
de verdade, o que ensinaremos mais à frente, mas por ora, é importante
conhecermos as ferramentas nativas para teste.
No exemplo acima eu já possuo dois dispositivos virtuais de teste
configurados.
Para criar um novo, clique no botão Create Virtual Device, que abrirá o
wizard de configuração do dispositivo. Neste wizard definimos todas as
características de hardware e software do nosso aparelho virtual.
Na primeira tela escolhemos a plataforma, “Phone”, o modelo de exemplo,
“Nexus One” e avançamos com o botão “Next”.
Na tela seguinte escolhemos a imagem do sistema que vamos utilizar no
emulador.
Por padrão o Android Studio vem com a imagem do Android mais recente
instalado, mas esta janela do wizard irá lhe listar mesmo as imagens que você
ainda não baixou, o que forçará o seu download automaticamente.
Apenas selecione o Android Lollipop para celulares com chip ARM que são
os mais indicados na época de escrita deste livro e avance novamente.
Na última janela do wizard temos a opção de definir o nome da máquina
virtual (que deve ser único e preferencialmente sem acentos), a escala da tela
(para que seja melhor exibido no seu computador, uma vez que alguns
dispositivos podem ter resoluções maiores que a do seu monitor).
Duas outras opções permitem usar a GPU física do computador para
aumentar a velocidade do processamento gráfico, enquanto que a segunda
permite criar snapshots, que é como se o emulador fosse hibernado ao invés
de desligado, possibilitando inicializações mais rápidas no futuro.
Antes de finalizar você pode ainda querer definir algumas configurações
avançadas clicando em “Show Advanced Settings”, como memória RAM do
dispositivo (512MB para Android 2 ou 1GB para posteriores é o suficiente),
câmera frontal/traseira (que pode ser configurada para usar sua webcam ou
uma imagem pré-definida), memória interna, cartão SD, teclado físico e por
aí vai.
Conforme necessitarmos de tais recursos de hardware voltaremos nesta parte
para configurá-los.
Clique em Finish e nossa VM será criada em poucos minutos, bastando clicar
no botão de Play para iniciar a emulação.
Atenção: A inicialização do Android pode demorar bastante, então não é
algo que irá querer vivenciar a cada vez que fizer uma alteração em seu
código. A dica é: após inicializar uma vez um AVD completamente, não o
feche, até que não tenha mais nada para programar em Android por hoje.
Deixe a janela do AVD aberta, inclusive podendo a fechar a janela do AVD
Manager. Quando for testar seu código Java no dispositivo virtual, ele já
estará pronto e irá executar mais rapidamente.
Agora voltando ao Android Studio, com nosso app de Olá Mundo pronto de
fábrica, vamos clicar no botão de executar nosso aplicativo (Run), que é um
ícone de Play na toolbar.

Quando clicamos neste botão uma compilação é realizada em nosso projeto e


quaisquer erros de compilação que existirem serão apresentados para que
você resolva antes de continuar.
Caso não existam erros de compilação (o que não quer dizer que seu app está
necessariamente funcionando) o Android Studio irá perguntar em qual
dispositivo o app será instalado para testes.
Note que ele lista tanto os dispositivos virtuais quanto os reais, caso algum
esteja plugado via USB no computador.
Ao marcar a opção “Use same device for future launches” fará com que o
Android Studio não lhe questione mais sobre qual dispositivo irá usar para
testes, usando sempre o mesmo. Como resultado, veremos nosso aplicativo
rodando no simulador Android recém-criado.
Caso você queira testar no seu smartphone e ele não esteja aparecendo na
lista de dispositivos certifique-se que:
● a opção Depuração USB está habilitada (USB Debugging). Ela fica
dentro de Opções do Desenvolvedor, um menu secreto em alguns
aparelhos, mas que geralmente abre quando tocamos várias vezes no
item Versão do Android.
● a opção Fontes Desconhecidas está habilitada (Unknown Sources). Ela
fica dentro da área de Segurança do Android.
● o cabo USB está devidamente conectado e o smartphone foi
reconhecido corretamente pelo seu sistema operacional. Muitos
modelos exigem instalação do driver de depuração, chamado ADB
Interface, que pode ser obtido no Google pesquisando juntamente com
o nome do seu modelo de smartphone
Várias são as razões pelas quais vale o esforço de realizar os passos acima e
testar seus apps diretamente no smartphone, mas a principal delas é a
performance.
É muito, mas muito mais rápido usar o smartphone para testes do que as
máquinas virtuais Android.
4 INTERFACE GRÁFICA
Vimos no capítulo anterior todo o básico para começarmos a utilizar o
Android Studio para criação e testes de apps simples.
Neste capítulo veremos o essencial sobre a construção da interface gráfica de
nossos apps e o funcionamento de seus controles para criar os efeitos
desejados.
Toda Activity (imagine uma tela por enquanto) que possui interface gráfica
deve carregar um arquivo de layout logo que é criada, como uma de suas
primeiras ações.
Isso é feito usando o método setContentView, que espera o identificador
único do layout que será usado, que fica em R.layout (basicamente o nome do
arquivo de layout sem a extensão .xml).
Código 3: disponível em luiztools.com.br/livro-android-fontes

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

Com isso dizemos que vamos exibir o layout activity_main.xml durante a


execução da Activity MainActivity.java e a partir da linha imediatamente
abaixo à esta chamada, passa a ser possível manipular todos os aspectos da
interface desta tela do app.
Layouts
Um layout define a estrutura visual para sua interface com o usuário. Cada
“tela” de um aplicativo Android possui um arquivo de layout. Na plataforma
Android, a aparência dos layouts são descritas em arquivos XML que ficam
na pasta res/layout do seu projeto, sendo que inicialmente o próprio Android
Studio já cria um layout padrão para você.

Em geral os layouts estão associados a uma classe Activity de nome


semelhante, mas também é possível criar layouts independente de código
Java, clicando com o botão direito do mouse sobre a pasta layout e
selecionando a opção New > Layout resource file.
Assim como na biblioteca Swing para criação de aplicações gráficas desktop
em Java, os arquivos de layout definem os elementos de maneira relativa, e
não absoluta como em plataformas visuais de desenvolvimento como Delphi
e Visual Basic.
Toda a organização dos componentes na tela, chamados em Java de widgets,
respeita a ordem prevista pelo gerenciador (ou gerenciadores) de layout que
estivermos utilizando.
Voltando ao exemplo da aplicação MainApplication que apenas imprime um
Hello World na tela, note como o arquivo XML de layout inicia com a
definição de um nó RelativeLayout.
Código 4: disponível em luiztools.com.br/livro-android-fontes

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>

Todo arquivo XML de layout deve começar indicando qual o gerenciador de


layout que irá utilizar e, por padrão, o Android Studio cria com o gerenciador
RelativeLayout (em algumas versões mais recentes ele te pergunta se deseja
usar ConstraintLayout, mas não usaremos ele no livro então não marque essa
opção), que é o que possui a maior responsividade de layout entre diferentes
resoluções de dispositivo. Isso garante que seu app irá se ajustar
dinamicamente a cada tela sem precisar reescrever seu código.
De qualquer forma, dentro do nó raiz do Layout temos seus atributos,
iniciados com “android:”, a saber:
● layout_width: largura do layout. O valor match_parent garante que ele
irá ocupar toda a largura da tela do dispositivo.
● layout_height: o mesmo que o anterior, mas para altura do app.
● paddingLeft: a margem esquerda que os componentes dentro desse
layout terão de respeitar.
● paddingRight, paddingTop, paddingBottom: o mesmo anterior, só que
para a direita, cima e embaixo, respectivamente.
Outras propriedades úteis ainda incluem:
● background: define uma imagem ou cor de fundo para a tela do
aplicativo. As cores são definidas através de constantes do Android,
como @android:color/black e @color/holo_blue_dark. Caso opte por
uma imagem, a mesma deve estar na pasta drawble do seu projeto, e é
referenciada aqui com a sintaxe @drawable/nomeDaImagem, sem a
extensão da mesma.
Esses atributos são independentes do tipo de layout que escolhermos para
nossa tela. As características específicas de cada layout manager serão
discutidas a seguir.
Atenção para unidades de medida: embora estejamos muito acostumados
com o uso de pixels (px) para medidas de resolução, em Android recomenda-
se o uso de pixels independentes de densidade (dp). Isso se deve ao fato de
que diferentes dispositivos possuem resoluções diferentes com densidade de
pixels por polegada também diferente. O uso de dp ao invés de px torna o
dimensionamento dos componentes mais relativo e menos absoluto, evitando
que alguns componentes fiquem muito grandes ou pequenos em dispositivos
com resoluções muito diferentes do que as originais dos testes.
Relative Layout
Relative Layout é o gerenciador de layouts recomendado para uso geral na
plataforma Android. Ele se baseia no conceito de posicionamento relativo de
elementos, onde a disposição dos componentes é dada em relação a outro
componente, incluindo a própria tela.
Quando arrastamos um widget qualquer para a tela, como um Button (que
veremos mais pra frente), possuímos uma série de atributos para especificar o
seu posicionamento relativo, como segue:
● layout_centerHorizontal: com true ou false você indica se seu
componente será centralizado horizontalmente na tela (em relação às
margens esquerda e direita do dispositivo)
● layout_centerVertical: o mesmo que o anterior, mas em relação às
margens superior e inferior do dispositivo
● layout_alignParentTop e layout_alignParentBottom: alinha este
componente em relação às extremidades superior ou inferior da tela
● layout_alignParentLeft e layout_alignParentRight: o mesmo que o
anterior, mas em relação à extremidades esquerda e direita da tela
● layout_below e layout_above: estas propriedades servem para indicar
o id do widget ao qual este widget está abaixo (below) ou acima
(above)
● layout_toRightOf e layout_toLeftOf: o mesmo que o anterior, mas
para indicar o id do widget ao este widget está à direita (toRightOf) ou
à esquerda (toLeftOf)
● layout_marginTop, layout_marginBottom, layout_marginLeft e
layout_marginRight: indica uma margem (em px ou dp) em relação ao
widget que está em cima, embaixo, à esquerda ou à direita,
respectivamente.
Para exemplificar o uso de alguns destes atributos, arraste 5 botões para
dentro do RelativeLayout da sua tela. Coloque os textos Centro, Norte, Sul,
Leste e Oeste na propriedade text deles. A ideia é dispô-los em formato de
cruz, como se fosse uma rosa-dos-ventos.
Para o botão com o texto “Centro” definiremos seus atributos para que ele
fique centralizado na tela, tanto vertical quanto horizontalmente, o que
garantirá que ele de fato seja o componente central e que a partir dele iremos
posicionar os demais elementos.
Código 5: disponível em luiztools.com.br/livro-android-fontes

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Centro"
android:id="@+id/btnCentro"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />

Note que foi definido um id para este componente, que chamamos de


btnCentro. Os ids são importantes pois é a partir deles que referenciamos os
componentes em nosso código Java e é a partir deles que posicionamos os
elementos em um Relative Layout.
Agora que temos nosso botão central posicionado bem no meio da tela,
vamos posicionar o nosso botão Norte, imediatamente acima do botão
Centro. Para isso, definimos que nosso botão Norte também está centralizado
horizontalmente, e que está acima (layout_above) do componente com id de
btnCentro (nosso botão Centro).
Código 6: disponível em luiztools.com.br/livro-android-fontes

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Norte"
android:id="@+id/btnNorte"
android:layout_centerHorizontal="true"
android:layout_above="@+id/btnCentro"/>

Mantivemos nosso padrão de definir os ids de maneira descritiva, usando o id


btnNorte para nosso botão Norte. Note como referenciamos o id do botão ao
qual estamos acima, usando a sintaxe “@+id/btnCentro”. Se fez tudo
corretamente, já deve começar a ter resultados gráficos positivos,
visualizando o botão Norte acima do botão Centro.
Para o botão sul, o trabalho é o mesmo, mas definindo o atributo
layout_below, que indica o id do elemento ao qual ele está abaixo e seu id
sendo definido como btnSul.
Código 7: disponível em luiztools.com.br/livro-android-fontes

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Sul"
android:id="@+id/btnSul"
android:layout_below="@+id/btnCentro"
android:layout_centerHorizontal="true" />

Agora para posicionarmos os botão Leste e Oeste, na direita e esquerda do


botão Centro (respectivamente), devemos centralizá-los verticalmente
(layout_centerVertical) e defini-los como estando abaixo do botão Norte e à
direita (RightOf) e esquerda (LeftOf) do botão Centro.
Código 8: disponível em luiztools.com.br/livro-android-fontes

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Leste"
android:id="@+id/btnLeste"
android:layout_below="@+id/btnNorte"
android:layout_toRightOf="@+id/btnCentro" />

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Oeste"
android:id="@+id/btnOeste"
android:layout_below="@+id/btnNorte"
android:layout_toLeftOf="@+id/btnCentro" />

Note que invés de dizermos que os botões Oeste e Leste estão abaixo do
botão Norte, poderíamos ter definido que eles estão acima do botão Sul, o
que provocaria o mesmo efeito. De qualquer forma, o objetivo final foi
alcançado, como mostra a imagem a seguir:

Linear Layout
O LinearLayout é um gerenciador de layout muito parecido com o
FlowLayout do Java Swing. Nele definimos a propriedade orientation como
sendo vertical ou horizontal e isso irá fazer com que todos componentes
sejam posicionados um embaixo do outro (vertical) ou um ao lado do outro
(horizontal).
Isto pode parecer um tanto inútil quando visto sob a ótica simplista de usar
apenas um gerenciador de layout por tela, mas está longe de ser verdade. O
LinearLayout é um gerenciador poderoso quando queremos montar telas com
componentes alinhados em linhas e colunas, como em uma tabela, mas muito
mais flexível.
Isso porquê o Android nos permite aninhar diferentes gerenciadores de
layouts para produzir os mais variados efeitos e um dos usos mais comuns
desse recurso é o de combinar LinearLayouts horizontais e verticais para criar
o efeito de linhas e/ou colunas, ou mesmo combinar RelativeLayouts com
LinearLayouts para criar layouts realmente responsivos, que se adaptem à
todo tipo de tela.
Para exemplificar, vamos criar um formulário onde tenhamos 3 linhas
imaginárias com elementos dispostos nelas, um ao lado do outro.Para isso,
devemos começar definindo o nosso layout do XML como sendo
LinearLayout com orientation vertical (você pode fazer isso selecionando
LinearLayout durante a criação de um novo Layout resource file).
Código 9: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

</LinearLayout>

Sendo um LinearLayout com orientação vertical, tudo o que colocarmos


dentro dele será posicionado em ordem vertical (um embaixo do outro). Por
isso, vamos adicionar três novos LinearLayouts dentro do primeiro, mas
todos com orientation=”horizontal”.
Código 10: disponível em luiztools.com.br/livro-android-fontes
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"></LinearLayout>

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"></LinearLayout>

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"></LinearLayout>

</LinearLayout>

Note como todos possuem a propriedade layout_width definida para


match_parent (o que garante a largura máxima igual da tela) e layout_height
como wrap_content (que aumenta a altura conforme o conteúdo do layout).
Atenção para match_parent e fill_parent: até a versão API 8 do Android, a
constante se chamava fill_parent, mas depois foi alterada para match_parent.
Ou seja, possuem o mesmo efeito, embora recomenda-se usar match_parent
por ser o termo mais novo.
Visualmente falando isso não representa nada ainda no simulador, pois os
layouts por si só não possuem aparência alguma. Mas é dentro de cada um
desses layouts que faremos nossas linhas imaginárias.
Para o primeiro LinearLayout, arraste um TextView seguido de um EditText,
como segue:
Código 11: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="150dp"
android:layout_height="50dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Nome:"
android:id="@+id/textView" />

<EditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="@+id/editText" />

</LinearLayout>

Note que defini manualmente a largura e altura do TextView para que fique
esteticamente agradável na tela do dispositivo, bem como a largura do
EditText para ocupar o máximo de espaço possível, enquanto a altura do
mesmo ficou fixa para condizer com a altura do TextView. Por fim coloquei
um texto no TextView definido como “Nome:” que geralmente serve como
rótulo para o campo à direita em formulários.
Agora, passemos à segunda linha imaginária, onde repetiremos o processo,
apenas trocando o rótulo do campo para “Endereço:”.
Código 12: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:layout_width="150dp"
android:layout_height="50dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Endereço:"
android:id="@+id/textView2" />

<EditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="@+id/editText2" />

</LinearLayout>
E por fim, nossa última linha imaginária terá apenas dois botões: um de
Cancelar e outro de Salvar, como segue:
Código 13: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Cancelar"
android:id="@+id/button"/>

<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Salvar"
android:id="@+id/button2"/>

</LinearLayout>

Com isso, teremos a construção a seguir:

Atenção com o editor visual: muitas vezes você notará que o editor visual
não é preciso e ficará brigando com ele durante várias horas. Não faça isso. O
melhor que você faz é arrastar os componentes visualmente para a tela e
depois ir ao editor XML para recortar, copiar, colar e configurar seus widgets
manualmente. Isso lhe dará um domínio e compreensão mais profundos da
construção de interfaces, além de ser muito mais assertivo.
Outro uso bem comum do LinearLayout é quando queremos distribuir a
largura ou altura dos componentes de maneira variável pela tela, proporcional
ao tamanho da mesma. Para isso usamos a propriedade layout_weight onde
definimos o “peso” (na verdade a importância) de cada componente dentro de
um LinearLayout, de maneira análoga a uma porcentagem. Para decidir se
queremos que a largura (width) ou altura (height) seja proporcional ao
tamanho da tela, devemos setar a característica correspondente (layout_width
ou layout_height) como 0dp.
No exemplo abaixo temos a seguinte configuração: três botões dentro de um
LinearLayout horizontal, com a propriedade layout_width como 0dp e
layout_weight com 3, 2 e 1, respectivamente do primeiro ao terceiro botão.
Com isso, o primeiro botão ocupará 3x mais espaço que o terceiro, enquanto
que o segundo será o meio termo. Isso garante que os mesmos se esticarão
horizontalmente para preencher a largura da tela, seja um smartphone ou uma
smarTV.

Se somarmos os pesos teremos o máximo de 6, ou seja, 6=100% da largura


da tela. O primeiro botão, que possui peso 3 ocupará 50%, o que possui 2
ocupará 35% e o último 15%, aproximadamente.
Resumindo: caso eu tenha dois botões e cada um deles deva ocupar 50% da
largura da tela, eu devo definir o layout_width de cada um como 0dp e definir
o layout_weight de ambos como 1, indicando que eles possuem pesos iguais.
Agora, se eu quiser que o primeiro botão tenha 66% da tela (2/3) enquanto
que o segundo tenha apenas 33%, basta definir o layout_weight do primeiro
para 2 e do segundo para 1.
Juntando LinearLayouts com RelativeLayouts nos traz grandes vantagens na
criação de interfaces responsivas às mais diferentes resoluções, sempre
priorizando a usabilidade dos apps. Mas como tudo na vida, a prática leva à
perfeição e você deve buscar desafiar-se a cada app que for desenvolver,
bolando novas e criativas maneiras de chegar às interfaces desejadas
combinando os layouts e suas propriedades.
Constraint Layout
Constraint Layout é o gerenciador de layouts mais recente da plataforma
Android. Ele é o resultado das melhores características existentes nos layouts
Relative e Linear, juntos em um layout só. Ele baseia-se no conceito de
posicionamento relativo de elementos (assim como o Relative), onde cada
componente está relativo à outro; e também possui preenchimento
proporcional de espaços (assim como no Linear), onde podemos definir de
maneira semelhante a porcentagem, o quanto de espaço cada componente vai
ocupar.
Atenção: Apesar de ser um recurso novo da plataforma, ele é distribuído
como uma biblioteca que oferece suporte às versões de Android 2.3 ou
superiores (API 9+). Caso esta opção de Layout não esteja aparecendo para
você, certifique-se de estar com as opções "ConstraintLayout for Android" e
"Solver for Contraint Layout" instaladas no seu SDK Manager (na seção
SDK Tools). Depois de instalar esses pacotes, você deve reiniciar o Android
Studio e a partir de então terá a opção de criar layouts com esse recurso.
Constraints significa algo como "restrições" ou "limitações", e são essas
restrições que são o cerne por trás do funcionamento deste layout manager,
sendo fundamentais para sua utilização entender elas primeiro.
Para definir a posição de um componente no ConstraintLayout você deve
adicionar ao menos uma constraint horizontal e uma vertical para a view.
Cada constraint representa uma conexão ou alinhamento em relação à outra
view, o layout parent ou mesmo uma linha-guia invisível (??). Cada
constraint define a posição da view a partir de seus eixos vertical e
horizontal; motivo pelo qual temos de definir no mínimo essas duas
constraints, embora seguidamente precisaremos de mais de duas para
conseguir os comportamentos desejados.
Quando você arrasta e solta uma view no Layout Editor, ela fica exatamente
onde você deixar ela, mesmo que não possua constraint alguma. No entanto,
isso é apenas para tornar o seu trabalho mais fácil quando estiver
posicionando os elementos; se uma view não possui constraints, ela ficará no
canto superior esquerdo da tela automaticamente (0,0).
Embora a falta de uma constraint não cause um erro de compilação, o Layout
Editor indicará a falta de constraints como um erro na toolbar. Para ajudar
você a não esquecer de constraints, o Layout Editor pode automaticamente
adicionar as constraints para você com os recursos Autoconnect e "Infer
Constraints" que são novidades na ferramenta e que mostrarei mais adiante.
Na imagem abaixo, eu arrastei um Button para o editor visual de interfaces.

Quando você solta o componente ele exibe bordas ao seu redor com ícones
quadrados para redimensionamento nos cantos do componente e ícones
circulares para constraints nas laterais dele.
Clique no componente para selecioná-lo. Então clique e segure um dos ícones
circulares (constraint handle) arrastando até um ponto de ancoragem
disponível (a face de outro componente, do parent layout ou uma linha-guia).
Quando você soltar, a constraint será criada, com uma margem default
separando os dois componentes, como mostra abaixo.

Outra alternativa é simplesmente arrastar seus componentes e usar os


recursos Autoconnect (ícone U) e Infer Constraints (ícone de estrelas
amarelas), acessíveis na toolbar logo acima do Layout Editor.

O resultado, independente do método utilizado, vai ancorar o seu Button em


alguma(s) extremidade(s) da tela ou ainda em outros componentes.
Quando estiver criando constraints, lembre-se das seguintes regras:
● Cada componente deve ter ao menos duas constraints: uma vertical e
uma horizontal.
● Você pode criar constraints somente entre um constraint handle (o
ícone circular na lateral) e um ponto de ancoragem que compartilhem
o mesmo plano, ou seja, vertical com vertical e horizontal com
horizontal.
● Cada constraint handle pode ser usado para apenas uma constraint,
mas você pode criar várias constraints (de diferentes componentes)
para um mesmo ponto de ancoragem.
Para remover uma constraint, selecione o componente e então clique no
constraint handle. Ou remova todas constraints selecionando a view e depois
clicando em "Clear Constraints" na toolbar (ícone do x vermelho).
Quando adicionamos outros componentes à este layout, podemos ancorar
estes novos componentes em outros já existentes, como fazíamos com
RelativeLayout no passado. No exemplo abaixo, ancorei o plano vertical do
segundo botão logo abaixo do primeiro botão.

Você notou que a linha da constraint fica "serrilhada" quando adicionamos


constraints às duas laterais do componente?
Neste caso elas atuam como âncoras opostas, cada uma puxando o
componente para o seu lado, o que resulta, por padrão, em uma centralização
mesmo, como podem ver na imagem anterior. Quando temos vários
componentes no mesmo plano (horizontal ou vertical) podemos adicionar
pesos à eles, bem como remover as margens default e muito mais técnicas
avançadas que veremos ao longo do livro.
Por ora, exercite o posicionamento de componentes na tela tentando montar
uma "cruz" com Buttons. Será que você consegue?

Calma, eu não puxei todas essas constraints aí na mão, apenas posicionei os


Buttons onde gostaria que eles ficassem e usei o recurso Infer Constraints da
toolbar do Layout Editor. Fácil, não?!
É bem comum a necessidade de fazer um ajuste fino depois de utilizar esse
recurso, pois ele tenta criar as constraints para posicionar os elementos de
maneira muito eficiente, mas não consegue acertar 100% das vezes.
Outros Gerenciadores de Layout
Existem outros gerenciadores de layout como GridLayout, TableLayout,
AbsoluteLayout e até FrameLayout, sendo este último utilizado quando
queremos fazer aquelas telas iniciais com apenas uma imagem ou coisa
parecida. Entretanto, devido à fragmentação da plataforma Android e
flexibilidade dos dois gerenciadores apresentados acima, aconselha-se o uso
dos mesmos para construção de seus apps.
Recentemente foi liberado o ConstraintLayout, que promete ser mais simples
que os demais. Aqui no livro eu não uso ConstraintLayout, mas ele é bem
intuitivo e com a explicação que dei anteriormente, você deve conseguir se
virar com ele.
Dica: para os que não se adaptaram com o editor de layout do Android
Studio, considere a utilização da ferramenta DroidDraw:
http://droiddraw.googlecode.com
Widgets Comuns
Durante os estudos dos gerenciadores de layout nas seções anteriores tivemos
que criar alguns componentes e até um formulário completo. Você já deve
saber o básico de arrastar e soltar componentes usando o editor visual e até
mesmo configurar os componentes e layouts usando o editor XML. Mas
agora vamos realmente estudá-los!
Widget é o nome dado aos componentes utilizados para compor uma
interface gráfica em Java. O termo vem de Windows + Gadgets, ou seja,
“bugigangas de janelas”. A seguir iremos ver alguns dos principais widgets
disponíveis para a plataforma Android e como utilizá-los em nossos apps.
Cada widget possui uma responsabilidade específica dentro dos aplicativos
Android e conhecer os essenciais lhe dará um conjunto poderoso de
ferramentas para a construção de apps corporativos.
Todo Widget possui uma série de propriedades que podem ser configuradas
para que ele aparente e se comporte da maneira desejada. Você pode
configurá-las pelo editor visual ou pelo editor XML, sendo este último bem
preciso e fácil de usar, por causa do recurso de code complete que permite ao
desenvolvedor digitar apenas parte do nome da propriedade e a ferramenta já
completa para você.
Todo widget possui essencialmente duas propriedades obrigatórias:
● layout_width: largura do componente, podendo ser fixa em dps ou
relativa como match_parent (preenche toda largura do container
externo) e wrap_content (expande conforme o conteúdo do widget)
● layout_height: altura do componente, que se comporta de maneira
análoga ao layout_width
Caso você deseje manipular (inclusive ler) este widget no seu código Java,
mais tarde, deverá também definir um ID único para cada um dos seus
componentes, usando a propriedade android:id com a seguinte
@+id/widgetId, substituindo widgetId pelo identificador único que deseje
(como de praxe, sem acentos, espaços ou hífen).
Atenção com Ids: além das regras básicas de que um id não pode ter acentos,
espaços ou hífen é interessante que além de não se repetirem na mesma tela
que eles não se repitam na mesma aplicação, para evitar confusões depois.
Outra dica é adotar um padrão de nomenclatura como prefixo + função,
muito comum entre diversos programadores.
TextView
O TextView é o componente padrão para exibição de textos estáticos (que
não permitem edição do usuário) em apps Android.
Chamado de Label na maioria das outras plataformas, possui essencialmente
a propridade text, que define o texto a ser exibido e as propriedades iniciadas
por ‘font’ que definem as configurações visuais do texto, como tamanho, cor,
família, etc.
O Android sugere trabalhar com alguns tamanhos pré-definidos de
TextViews como Small, Medium e Large, que são setados na propriedade
textAppearance e são tamanhos relativos que funcionam bem em diferentes
resoluções de dispositivos. Ou seja, um Large TextView sempre terá um
tamanho relativamente grande, independente se está sendo visualizado em
um tablet ou smartphone, lhe poupando o redimensionamento dinâmico dos
textos.
Para colocar um TextView em um formulário, você deve arrastá-lo da paleta
para dentro do seu layout, obtendo o código XML que segue (neste exemplo
foi usado um Large Text):
Código 14: disponível em luiztools.com.br/livro-android-fontes

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Texto Grande"
android:id="@+id/lblTeste" />

Note que defini a propriedade text como “Texto Grande” e a propriedade id


como “@+id/lblTeste”. O resultado esperado é bem simples, como segue:
Para manipular este TextView através do nosso código Java, devemos criar
uma variável do tipo TextView em nossa Activity, carregando-a com o
método findViewById, que mostra a importância de definirmos bons ids para
nossos controles, visando nos lembrarmos deles depois. Esse tipo de
carregamento somente funciona se o controle existir no XML que foi
carregado pelo setContentView, bem como se chamarmos o controle após o
carregamento do setContentView, como no exemplo.
Código 15: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);

TextView lblTeste = (TextView)findViewById(R.id.lblTeste);


String teste = lblTeste.getText().toString();
lblTeste.setText("Outro teste");
}

Neste exemplo, carregamos a lblTeste e depois lemos seu conteúdo, para em


seguida trocarmos ele usando os métodos getText (leitura) e setText (escrita).
Note que o método getText() do TextView não retorna uma String, mas sim
um CharSequence. Basta convertermos o mesmo com o bom e velho
toString() e o problema está resolvido.
EditText
Tão comum e útil quanto os TextViews, os controles EditText são campos de
texto que podem ser manipulados tanto via código quanto pelo usuário. O que
diferem os EditText do Android dos TextBox tradicionais de outras
plataformas é o uso do teclado virtual. Muito comum em smartphones, os
teclados virtuais possibilitam que dispositivos touch exibam um teclado
completo para digitação nos campos, poupando custos de hardware (com
teclados físicos) e proporcionando uma experiência mais intuitiva.
Código 16: disponível em luiztools.com.br/livro-android-fontes

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text=""
android:id="@+id/txtTeste"/>

Uma vez que a digitação em teclados virtuais nem sempre é tão prática
quanto em teclados convencionais, onde temos mais espaço para posicionar
as teclas (e mãos), o Android fornece a opção de definir o tipo de entrada que
vai ser disponibilizada para o usuário, como textos numéricos, nomes
próprios, senhas, etc, o que muda automaticamente o comportamento do
teclado virtual, que passa a exibir primariamente os números ou letras, por
exemplo. Na paleta de componentes estão disponíveis uma série de
componentes EditText pré-configurados e você deve usá-los conforme a
necessidade, sendo que suas configurações básicas não mudam. Arraste um
para um layout XML e veja o que acontece ao lado.
A propriedade inputType define o tipo de texto que planejamos colocar nesse
EditText. No caso de textPersonName, o próprio teclado virtual vai gerenciar
o uso de maiúsculas e minúsculas nas palavras que irão compor o nome
próprio que será escrito, sem a necessidade de Shift ou Caps-Lock. Outros
inputTypes possíveis incluem numberDecimal, que faz com que o teclado
virtual já venha com os números à mostra.
Independente do inputType do EditText, a sua manipulação através de código
Java é idêntica. Sempre que quiser definir o texto presente no EditText ou ler
a informação que o usuário inseriu lá, use o seguinte código Java:
Código 17: disponível em luiztools.com.br/livro-android-fontes

EditText txtTeste = (EditText)findViewById(R.id.txtTeste);


String texto = txtTeste.getText().toString();
txtTeste.setText("Novo teste");

Neste exemplo, carregamos o EditText usando método findViewById,


disponível em todas Activities do seu projeto, passando o id do controle que
estamos carregando. Imediatamente abaixo podemos manipular qualquer
propriedade do EditText, incluindo a notória propriedade text, através dos
métodos get e set definidos na especificação Java.
Atenção sobre foco: é possível colocar no editor XML uma marcação (tag)
especial para dizer que um EditText requer o foco da tela (i.e. será o primeiro
a ser preenchido), conforme mostrado abaixo.
Código 18: disponível em luiztools.com.br/livro-android-fontes

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content">
<requestFocus />
</EditText>

Entretanto, caso o EditText seja o primeiro campo do formulário, ele


automaticamente será o foco da tela. Algumas vezes não queremos isso,
como em barras de pesquisa, e para desabilitar este recurso automático basta
definirmos no layout imediatamente acima as propriedades focusable=true e
focusableInTouchMode=true.
Em geral você irá usar TextViews como rótulos e EditTexts como entradas de
texto, em uma combinação muito comum de ser vista em formulários de
qualquer plataforma (web, desktop e mobile). Entretanto, o controle EditText
também lhe permite incluir um rótulo temporário dentro do mesmo, como os
populares placeholders da web, usando a propriedade android:hint. Um hint
fica visível com o texto que você definir até que o usuário toque no campo e
preencha o seu valor personalizado. Isto pode ser utilizado tanto para dar
mais informações ao usuário quanto para eliminar por completo o uso de
rótulos e criar formulários que usem melhor dos espaços da tela, como no
exemplo abaixo.

Button e ImageButton
Nenhum formulário está completo sem botões. Seja para salvar, cancelar,
limpar ou validar um formulário, o widget Button pode ser usado. A
diferença crucial entre o Button tradicional e o ImageButton é que o segundo
pode possuir uma imagem criada por você ao invés do tradicional formato
retangular com texto dentro. No mais, são exatamente iguais.
Arraste um Button para seu formulário (sendo que o Android define alguns
tamanhos pré-definidos de botões na paleta que podem lhe ajudar) e teremos
o seguinte código no editor XML.
Código 19: disponível em luiztools.com.br/livro-android-fontes

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Botão"
android:id="@+id/btnTeste" />

Não há muito o que configurar em um botão tradicional. Você pode definir


seu layout_width e layout_height (largura e altura do botão), seu text, seu id
(para ser usado depois no código Java) e talvez até seu background (cor de
fundo). Independente de qualquer configuração, é certo que você irá querer
que seu botão dispare algum código Java quando clicado. Para fazer isso,
vamos modificar levemente o código apresentado acima para incluir uma
propriedade muito importante, a onClick:
Código 20: disponível em luiztools.com.br/livro-android-fontes

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Botão"
android:id="@+id/btnTeste"
android:onClick="exibirMensagem" />

Na propriedade onClick definimos o nome do método público da Activity


que será executado quando este botão for clicado. Caso este método não
exista, isso irá gerar um erro no app, então vamos até o código de nossa
Activity para programar este método, que deve conter o mesmo nome
colocado na propriedade onClick, bem como apenas um parâmetro, do tipo
View (essa é uma obrigação do Android).
Código 21: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cadastro);
}

public void exibirMensagem(View v){


Toast.makeText(this, "Exemplo!", Toast.LENGTH_LONG).show();
}
O trecho de código que foi colocado dentro do método exibirMensagem é o
chamado Toast em Android. Um Toast (“torrada”) é uma mensagem de alerta
que aparece e some sozinha, sem interação do usuário. Os três parâmetros do
método estático makeText são o contexto de execução, a mensagem e o
tempo de duração do Toast na tela. Quando botão for clicado, irá exibir o
Toast, como na imagem a seguir.

Como você já deve ter entendido, para cada botão que eu quiser que seja
disparado em minha Activity, terei de criar um método para tratar o evento
onClick dele. Embora essa não seja a única maneira de fazer a ligação do
evento onClick como código Java, sem sombra de dúvida é a mais fácil.
Também é possível criar apenas um método para tratar todos os botões, pois
o parâmetro view contém todas as informações sobre qual o botão que
disparou este método. Entretanto esta abordagem não é usual, a menos que
muitos botões tenham comportamentos muito parecidos, como em uma
calculadora.
Atenção com o Toast: é muito comum os programadores esquecerem que o
Toast exige a chamada do método show() para que apareça de fato na tela do
app.
Para finalizar, com relação ao ImageButton, a única alteração a ser feita é a
exibição de uma imagem que já deve estar em seu projeto, na pasta de
res/drawable, como esta abaixo, por exemplo (vip.png). Cada imagem deve
ter um nome único (como de praxe, não pode conter acentos, hífen ou
espaços) e este nome servirá como identificador único da mesma na
aplicação.

Configuramos a imagem no botão da seguinte maneira:


Código 22: disponível em luiztools.com.br/livro-android-fontes

<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/salvar"
android:onClick="salvar"
android:id="@+id/btnImagem"/>

Note que a propriedade src define qual a imagem que vai ser utilizada, sem a
sua extensão (neste caso, salvar.png). Note também que o ImageButton
também possui uma propriedade onClick, que define o método Java a ser
executado na Activity que carregar este layout, assim como o botão
tradicional.
Atenção sobre imagens: imagens de botões geralmente são ícones que
ilustram o que aquele botão faz, como um disquete para salvar ou uma lixeira
para excluir. Um bom site para baixar ótimos ícones gratuitos é o
http://www.iconfinder.com e outro é o http://www.findicons.com.
Você também pode carregar a imagem dinamicamente a partir da web ou a
partir do cartão SD do dispositivo do usuário, em tempo de execução. Para
isto, veja mais à frente o funcionamento do carregamento dinâmico de
imagens como o widget ImageView, que é idêntico ao ImageButton, com
exceção de que não dispara eventos ao ser clicado.
Spinner
Muitas vezes precisamos exibir diversas opções aos usuários mas temos um
espaço limitado para tal. Um Spinner é um widget que permite que o usuário
selecione apenas uma opção dentre inúmeras disponíveis, como uma lista de
estados ou de profissões. Em alguns frameworks este controle também é
chamado de ComboBox ou DropDownList.
Arraste um Spinner para um layout XML e vamos configurá-lo!
Código 23: disponível em luiztools.com.br/livro-android-fontes

<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/spinner" />

As opções que serão listadas no Spinner podem ser configuradas via arquivo
XML ou via código Java. Para configurar via arquivo XML (maneira mais
fácil) você deve criar dentro da pasta res/values um novo arquivo XML
(Values resource files) com o nome de estados.xml (por exemplo).
Coloque o seguinte conteúdo dentro dele:
Código 24: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<resources>
<string-array name="estados">
<item>RS</item>
<item>SC</item>
<item>PR</item>
</string-array>
</resources>

Neste exemplo coloquei apenas os 3 estados do sul do país para não ficar tão
extenso, mas você deve ter entendido a ideia. Aqui criamos um array de
Strings com o nome de “estados”, contendo diversos itens, sendo cada item a
sigla de um estado.
Agora no Spinner, para linkarmos o XML de estados com o widget, usamos a
propriedade entries, como segue:
Código 25: disponível em luiztools.com.br/livro-android-fontes

<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/spinner"
android:entries="@array/estados"/>

Com isso, carrega-se o conteúdo do arquivo estados.xml para dentro do


Spinner durante a criação da tela no app, automaticamente, como mostra na
imagem abaixo.

A outra maneira de carregarmos os itens do Spinner é através de código Java.


A primeira opção é ótima quando sua lista possui um número pré-definido de
elementos e isto não será alterado no decorrer do uso do app. Entretanto, em
alguns casos é necessário carregar os dados de alguma fonte externa, ou gerá-
los dinamicamente por algum motivo.
A criação de um spinner dinâmico é idêntico ao que foi mostrado antes,
exceto que não definimos a propriedade entries do mesmo no XML e nem
precisamos criar um arquivo XML como feito com os estados. A grande
mágica se dá no código Java onCreate da Activity, como segue:
Código 26: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cliente);

String[] array = {"RS","SC","PR"};


ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, array);
Spinner spinner = (Spinner)findViewById(R.id.spinner);
spinner.setAdapter(adapter);
}

Note que carregamos o spinner através do método findViewById buscando


pelo id definido no XML de layout. Logo abaixo, temos um array estático de
Strings com os estados do sul/sudeste do Brasil. Note que este array pode ser
gerado dinamicamente ou mesmo criado através de uma fonte externa, para
que na linha de baixo, possamos inseri-lo em um ArrayAdapter. O Android
trabalha com ArrayAdapters para adequar cada elemento de um array a um
layout de item e usamos para isso um layout padrão do Android denominado
android.R.layout.simple_spinner_item, embora existam diversas outras
opções e inclusive podemos criar o nosso próprio layout de item para spinner.
O efeito é o mesmo do método anterior, como mostrado a seguir.
Caso a gente queira descobrir qual item que foi selecionado, no clique de um
botão Salvar, por exemplo, basta utilizarmos o método getSelectedItem() do
Spinner, que retorna um object que facilmente pode ser convertido para
String usando toString(). O exemplo abaixo também mostra como podemos
definir, via código Java, qual será o elemento selecionado do Spinner.
Código 27: disponível em luiztools.com.br/livro-android-fontes

//carrega o spinner em uma variável local


Spinner spinner = (Spinner)findViewById(R.id.spinner);
//pega o item selecionado do Spinner em formato texto
String uf = spinner.getSelectedItem().toString();
//muda o item selecionado do spinner passando a posição
spinner.setSelection(1);

E por último, mas não menos importante, existem ocasiões em que você quer
que alguma ação seja realizada toda vez que um item for selecionado
(imediatamente). Nestes casos, você deve linkar o controle spinner com um
código Java em sua Activity. Não há qualquer alteração a ser realizada no
XML do exemplo de carregamento do Spinner, apenas no código Java da
Activity, como segue.
Código 28: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cliente);

String[] array = {"RS","SC","PR"};


ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, array);
final Spinner spinner = (Spinner)findViewById(R.id.spinner);
spinner.setAdapter(adapter);

spinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView<?> parent, View view, int
position, long id) {
String uf = spinner.getSelectedItem().toString();
Toast.makeText(getBaseContext(), "UF selecionado: " + uf,
Toast.LENGTH_LONG).show();
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
});
}

Note como foi criado uma variável Spinner no escopo da classe mas fora do
evento onCreate. Isso garante que a variável seja carregada apenas uma vez
no onCreate e chamada quantas vezes precisar dentro a activity. Este evento
será disparado toda vez que um item do spinner for selecionado.
Para o código a ser executado, apenas um Toast exibindo o estado que foi
selecionado. Note que poderia ser a chamada Java que você quisesse, como
por exemplo carregar outro spinner na sequência com as cidades daquele
estado, por exemplo, ou ocultar/exibir outro campo.
O Spinner é um componente muito poderoso e ele permite inclusive que
possamos criar layouts inteiramente personalizados. Claro, essa não é uma
tarefa muito trivial e é de necessidade duvidosa uma vez que listagens
complexas deveriam ser ListViews (ver mais adiante, na seção de
Containers). Entretanto, existe mais uma coisa “avançada” que devemos
aprender antes de avançar para o próximo widget: Spinners com chave-valor!
Basicamente queremos que o Spinner exiba um texto, como o nome do
estado, mas quando a gente quiser ler o que foi selecionado, queremos pegar
um código, como a sigla do estado. Como fazer isso? É mais fácil do que
parece e primeiramente iremos precisar criar uma classe Estado no package
principal do nosso app. Nossa classe Estado, que está ilustrada abaixo, possui
apenas dois atributos (nome e sigla), um construtor e a sobrecarga do método
toString forçando os objetos de Estado a retornarem o seu nome quando
forçados a se tornarem Strings. Por quê? Veremos daqui a pouco, apenas
confie em mim por enquanto!
Código 29: disponível em luiztools.com.br/livro-android-fontes

public class Estado {


private String nome;
private String sigla;

public Estado(String nome, String sigla){


this.nome = nome;
this.sigla = sigla;
}

public String getNome(){ return this.nome; }


public String getSigla(){ return this.sigla; }

@Override
public String toString(){ return this.nome; }
}
A seguir, o código de carregamento do nosso ArrayList de estados, que
obviamente poderia ser carregado a partir de um banco de dados ou
webservice. Por ora, deixaremos estático dentro do evento onCreate de uma
Activity qualquer (a mesma que terá o Spinner de estados). Após
carregarmos os estados, criamos nosso ArrayAdapter, mas desta vez ao invés
de usarmos Strings simples usaremos um ArrayAdapter<Estado> que permite
que coloquemos o nosso List<Estado> dentro, para em seguida carregar
nosso Spinner e definirmos o adapter com setAdapter.
Código 30: disponível em luiztools.com.br/livro-android-fontes

List<Estado> array = new ArrayList<>();


array.add(new Estado("Rio Grande do Sul", "RS"));
array.add(new Estado("Santa Catarina", "SC"));
array.add(new Estado("Paraná", "PR"));

ArrayAdapter<Estado> adapter = new ArrayAdapter<>(this,


android.R.layout.simple_spinner_item, array);
final Spinner spinner = (Spinner)findViewById(R.id.spinner);
spinner.setAdapter(adapter);

Como pode ter notado, este trecho é praticamente idêntico ao do Spinner


normal que usa apenas Strings. A diferença maior se dará em seu
comportamento, então vamos programar o evento de item selecionado do
Spinner:
Código 31: disponível em luiztools.com.br/livro-android-fontes

spinner.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int
position, long id) {
Estado uf = (Estado)spinner.getSelectedItem();
Toast.makeText(getBaseContext(), "UF selecionado: " + uf.getSigla(),
Toast.LENGTH_LONG).show();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {

}
});

Note que ao usar o método getSelectedItem() sobre o objeto spnEstados


conseguimos obter acesso ao objeto Estado original e com isso podemos
acessar qualquer outra de suas características e métodos públicos, como
getSigla()! Agora olhe o que acontece quando executamos o app e listamos
os estados existentes:

Funcionou perfeitamente! Isso porque ao jogar quaisquer objetos dentro do


adapter de um Spinner nós forçamos eles a retornarem as suas Strings através
do método toString de cada um (afinal, toString é definido na classe Object,
superclasse de todas classes Java). Ou seja, o que implementarmos no método
toString de nossa classe Estado será o que aparecerá no Spinner, enquanto
que a sigla somente será visível para o código Java! Muito útil, certo?!
RadioButton e RadioGroup
Em algumas situações você quer disponibilizar ao usuários algumas opções,
sendo que ele apenas pode selecionar uma delas. Quando são realmente
poucas, como opções de sexo (masculino e feminino) um RadioGroup com
RadioButtons pode solucionar seu problema.
Como mencionado, o primeiro passo é arrastar um RadioGroup para seu
layout XML.
Código 32: disponível em luiztools.com.br/livro-android-fontes

<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/grpSexo">
</RadioGroup>

O RadioGroup tem poucos atributos a serem configurados, como orientation


(que indica se os RadioButtons dentro dele serão alocados horizontalmente
ou verticalmente) e o tradicional id. Dentro do RadioGroup é que arrastamos
nossos RadioButtons, como segue, o que garantirá que apenas um deles seja
selecionado de cada vez.
Código 33: disponível em luiztools.com.br/livro-android-fontes

<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/grpSexo">

<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Masculino"
android:id="@+id/radMasculino"/>

<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Feminino"
android:id="@+id/radFeminino"/>

</RadioGroup>
Como efeito temos a seguinte tela renderizada.

E para descobrir qual dos dois RadioButtons está selecionado, use o código
abaixo, onde carregamos cada um dos RadioButtons em variáveis para usar o
método isSelected() que retorna true ou false, dependendo do estado do
RadioButton.
Código 34: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cliente);

RadioButton radMasculino =
(RadioButton)findViewById(R.id.radMasculino);
RadioButton radFeminino =
(RadioButton)findViewById(R.id.radFeminino);
if(radMasculino.isSelected()) {
//o sexo masculino foi selecionado durante o cadastro
}
else if(radFeminino.isSelected()){
//o sexo feminino foi selecionado durante o cadastro
}
}

Caso você queira saber em tempo real quando um RadioButton for


selecionado, terá de manipular o evento onCheckedChanged de cada
RadioButton, como segue, preferencialmente no evento onCreate da Activity.
Tudo que você colocar dentro do método onCheckedChanged será executado
toda vez que aquele RadioButton do sexo Masculino for selecionado.
Código 35: disponível em luiztools.com.br/livro-android-fontes

radMasculino.setOnCheckedChangeListener(new
RadioButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean
isChecked) {
//esse método dispara no instante que o radio masculino for selecionado!
}
});

CheckBox
Um CheckBox é um componente composto por uma caixa que pode estar
selecionada ou não e um texto descritivo da característica associada à caixa.
Utilizamos Checkboxes quando queremos que o usuário informa Sim ou Não
para uma afirmação ou característica, como “Desejo receber e-mails com
informativos” ou “Este cliente é VIP”. Seja qual for o objetivo, para começar
você deve arrastar um CheckBox para seu layout.
Código 36: disponível em luiztools.com.br/livro-android-fontes

<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Desejo receber informativos"
android:id="@+id/chkReceberInformativos"/>

Poucos atributos necessários para configurar um CheckBox, sendo um deles


o tradicional id, o text da mensagem e um atributo checked, que indica se o
checkbox está selecionado ou não por padrão. O resultado esperado pode ser
visto na imagem abaixo:
E para ler esta informação, deve-se carregar o CheckBox em uma variável e
chamar o método isSelected() que diz se ele está ou não selecionado.
Código 37: disponível em luiztools.com.br/livro-android-fontes

//carrega o widget para uma variável local


CheckBox chkReceberInformativos =
(CheckBox)findViewById(R.id.chkReceberInformativos);
//obtém a informação se foi selecionado ou não
boolean selecionado = chkReceberInformativos.isSelected();
//altera o estado selecionado do checkbox
chkReceberInformativos.setSelected(true);

Existindo a necessidade de saber em tempo real quando um CheckBox é


selecionado, temos de mapear o evento OnCheckedChanged, como segue.
Todo o código que for colocado dentro do evento será executado sempre que
o CheckBox for selecionado ou de-selecionado.
Código 38: disponível em luiztools.com.br/livro-android-fontes

chkReceberInformativos.setOnClickListener(new
CheckBox.OnClickListener() {
@Override
public void onClick(View v) {
boolean selecionado = chkReceberInformativos.isSelected();
//método dispara no instante em que o checkbox foi clicado
}
});
Nota sobre Spinner, RadioGroup e CheckBox: uma pergunta comum é:
quando devemos utilizar cada um deles? Na verdade, faça-se a pergunta:
quantas opções o usuário deve ter? Se o usuário tem apenas as opções Sim e
não, o CheckBox é a melhor alternativa, ou no máximo o RadioGroup. Se o
usuário tiver até umas três opções, o Radiogroup é uma boa pedida. Mais do
que isso, mas dentro de poucas dezenas use um Spinner. Note que se o
conjunto de opções realmente for grande ou muito dinâmico, não tem saída,
você terá de usar um EditText, para permitir que o usuário tenha mais
liberdade ou faça combinações de Spinners, como a divisão clássica
estado/cidade que é muito mais inteligente do que listar as quase 5 mil
cidades brasileiras pro usuário escolher uma.
ImageView
O controle ImageView pode ser utilizado tanto para exibir uma imagem
estática, que esteja na pasta res/drawable (neste exemplo eu possuo uma
arsenal.png) quanto uma imagem dinâmica, carregada do smartphone. O
primeiro exemplo é mais simples, e começa arrastando-se um ImageView
para o layout XML e configurando seu atributo src com o nome da imagem
sem a extensão, neste caso, arsenal.png.
Código 39: disponível em luiztools.com.br/livro-android-fontes

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/arsenal"
android:id="@+id/imageView"/>

Neste exemplo não limitamos as dimensões da imagem e por isso ela ocupará
a tela inteira pois era muito grande. Caso queira limitar as dimensões da
mesma, use as propriedades maxHeight e maxWidth.
A outra opção que temos é carregar dinamicamente a imagem através de
código Java. Ainda assim, a imagem deve estar no seu telefone, seja no
armazenamento interno ou cartão SD. Para carregar imagens da Internet você
teria de baixar a imagem primeiro para o disco ou uma variável antes de ser
configurada na ImageView. Uso de recursos da Internet será mostrado em
capítulos posteriores.
Independente disso, o código Java abaixo pode lhe ajudar a entender como
carregar uma imagem dinamicamente:
Código 40: disponível em luiztools.com.br/livro-android-fontes

ImageView imageView = (ImageView)findViewById(R.id.imageView);


File storageDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOA
String foto = storageDir + File.pathSeparator + "imagem.jpg";
Bitmap bitmap = BitmapFactory.decodeFile(foto);
imageView.setImageBitmap(bitmap);

Outra alternativa, muito mais simples, porém não tão dinâmica, é ter a
imagem que se deseja exibir na pasta drawables do projeto e chamá-la pelo id
único no código da Activity que carrega a imagem na ImageView, como
abaixo, onde existe uma imagem florianopolis.jpg na pasta drawable do
projeto (e que o id é mapeado pela classe R dentro de R.drawable):
Código 41: disponível em luiztools.com.br/livro-android-fontes

ImageView imageView = (ImageView)findViewById(R.id.imageView);


imageView.setImageResource(R.drawable.imagem1);

Existem diversos outros widgets que não serão abordados neste livro como
ProgressBar, SeekBar e WebView, além da categoria Date & Time que
possuem documentações completas no site oficial
http://developer.android.com
Containers
Containers são um tipo especial de widget que permite que outros widgets
sejam inseridos no seu interior. O RadioGroup que usamos anteriormente
para agrupar os RadioButtons é um exemplo de container bem específico,
que aceita apenas RadioButtons dentro dele. Geralmente os containers por si
só não possuem grandes efeitos no app, mas quando utilizados em conjunto
com outros widgets são extremamente poderosos.
ListView
ListView é um grupo de views que exibe uma lista de itens “roláveis”. Os
itens da lista são automaticamente inseridos na lista usando um Adapter que
puxa o conteúdo da fonte como um array ou base de dados e converte cada
item em uma view colocada na lista.
Você pode adicionar uma ListView como um widget normal, em qualquer
layout dando um id para que possamos programá-la depois. Poucas
propriedades precisam de atenção, e uma delas é colocar um id único na sua
ListView, assim como faz com qualquer widget. Neste exemplo a Activity
terá o layout abaixo, onde temos um campo de busca com botão no topo e
uma ListView logo abaixo.
Código 42: disponível em luiztools.com.br/livro-android-fontes

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<EditText
android:layout_width="fill_parent"
android:layout_height="45dp"
android:maxLength="10"
android:id="@+id/txtPesquisa"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />

<Button
android:layout_width="80dp"
android:layout_height="45dp"
android:text="Buscar"
android:onClick="btnBuscar_OnClick"
android:id="@+id/btnBuscar"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true" />

<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/lstResultados"
android:layout_below="@+id/txtPesquisa"
android:layout_marginTop="10dp"/>
</RelativeLayout>

Visualmente este XML se transforma no seguinte layout:


Considerando o layout XML acima, programar a funcionalidade desta
activity se torna muito simples, começando pelo carregamento dos widgets
em variáveis Java locais (com o uso do findViewById), e criando o
comportamento de click do botão através do método btnBuscar_OnClick
(assim como passado no XML), que irá alterar os elementos da lista.
Código 43: disponível em luiztools.com.br/livro-android-fontes

ListView lstResultados;
EditText txtPesquisa;
Button btnBuscar;
String[] cidades = { "Porto Alegre", "Florianópolis", "Curitiba", "São
Paulo" };

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

txtPesquisa = (EditText)findViewById(R.id.txtPesquisa);
btnBuscar = (Button)findViewById(R.id.btnBuscar);
lstResultados = (ListView)findViewById(R.id.lstResultados);
}

public void btnBuscar_OnClick(View v){


String busca = txtPesquisa.getText().toString();
List<String> encontradas = new ArrayList<>();
for(String cidade : cidades){
if(cidade.contains(busca)) encontradas.add(cidade);
}
ArrayAdapter<String> adapter =
new ArrayAdapter<>(getBaseContext(),
android.R.layout.simple_list_item_1, encontradas);
lstResultados.setAdapter(adapter);
}

Neste exemplo temos um array de cidades. Quando o usuário digita uma


palavra e clica no botão de buscar, o método btnBuscar_OnClick é disparado,
procurando pelas cidades com aquela palavra e adicionando-as na ListView
através de um ArrayAdapter, provocando o efeito desejado. Obviamente este
array de cidades estático poderia ser carregado através de uma API ou um
banco de dados local, como preferir e como veremos também mais tarde.
Note que na imagem abaixo Porto Alegre não é listada, uma vez que não
possui ‘a’, mas sim ‘A’. Estes e outros problemas de busca, muito comuns
em apps corporativos, serão tratados mais à frente, quando começarmos a
falar de boas práticas.
E por último, o que acontece caso queiramos executar um código Java
quando um item de nossa ListView for selecionado? Basta adicionar um
Listener ao evento OnItemClick em nossa lstResultados, como no código
abaixo. Note que essa maneira de fazer binding aos eventos é padrão para
qualquer evento de widget, com exceção do onClick do Button, que pode ser
atribuído TAMBÉM via propriedade.
Código 44: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

txtPesquisa = (EditText)findViewById(R.id.txtPesquisa);
btnBuscar = (Button)findViewById(R.id.btnBuscar);
lstResultados = (ListView)findViewById(R.id.lstResultados);
lstResultados.setOnItemClickListener(new
ListView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int
position, long id) {
String cidade = lstResultados.getItemAtPosition(position).toString();
Toast.makeText(getBaseContext(), "Item " + cidade,
Toast.LENGTH_LONG).show();
}
});
}

O evento onItemClick definido dentro do OnItemClickListener irá ser


disparado toda vez que um item for selecionado, capturando o item
selecionado com o método getItemAtPosition + a position que o próprio
evento receber por parâmetro e exibindo em um Toast, que é apenas uma
caixa de diálogo genérica e sem qualquer interação. Caso os elementos do
ListView não sejam apenas Strings, serão necessárias algumas conversões
adicionais.
Caso o desenvolvedor deseje tratar de maneira diferenciada os clicks curtos
dos longos (quando você segura o dedo sobre um item por dois segundos),
pode-se usar outro listener, o OnItemLongClickListener, de forma muito
análoga. Isso é bem comum quando queremos exibir menus com ações a
serem executadas sobre cada um dos itens, por exemplo.
ListView com Objetos e Layout Personalizado
E por fim, podemos dizer que este tópico é sobre ListView no modo “Hard”.
Em certas circunstâncias não queremos listar apenas uma lista de Strings, mas
sim uma lista de objetos, com diversas informações em cada item. Para piorar
a situação, estas informações possuem um layout específico, com diferentes
fontes, cores, etc. É aí que o bicho pega!
Para definir um layout personalizado para os itens de uma ListView,
independente se é uma listview de tela cheia ou compondo uma interface com
outros componentes, você precisará criar um novo layout XML apenas para
um item de modelo. O processo é simples: clique direito do mouse sobre a
pasta de layouts e mande criar um novo layout XML file com o nome de
item_modelo e com o gerenciador RelativeLayout. Nosso layout exibirá o
nome da cidade, a sigla do estado, uma descrição da cidade e uma imagem
pequena (thumb), como na imagem abaixo.
Para chegar no layout acima, precisaremos adicionar em nosso
item_modelo.xml dois componentes TextView (large), um TextView
(medium) e um ImageView, definindo as propriedades de orientação para que
eles fiquem dispostos da maneira acima. Note que as imagens e informações
que serão utilizadas foram pegos da Internet e podem ser substituídas pelo
que você quiser (as imagens vão na pasta drawable, como qualquer outra). O
seu XML deve se parecer com o abaixo:
Código 45: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imgCidade"
android:src="@drawable/florianopolis"
android:layout_marginRight="10dp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Florianópolis"
android:id="@+id/lblCidade"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/imgCidade" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="/SC"
android:id="@+id/lblUF"
android:layout_alignParentTop="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/lblCidade"/>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Capital de Santa Catarina"
android:id="@+id/lblDescricao"
android:textColor="@android:color/darker_gray"
android:layout_below="@+id/lblCidade"
android:layout_toRightOf="@+id/imgCidade"/>
</RelativeLayout>

Agora que temos o layout personalizado que será utilizado para renderizar os
itens da nossa ListView, é hora de criarmos a classe cujos objetos irão
guardar as informações a serem exibidas. Nossa classe Cidade deverá se
parecer com a abaixo:
Código 46: disponível em luiztools.com.br/livro-android-fontes

public class Cidade {

private String nome;


private String descricao;
private String uf;
private int resIdImagem;

public Cidade(String nome, String descricao, String uf, int resIdImagem){


this.nome = nome;
this.descricao = descricao;
this.uf = uf;
this.resIdImagem = resIdImagem;
}

public String getNome(){ return this.nome; }


public String getDescricao(){ return this.descricao; }
public String getUf(){ return this.uf; }
public int getIdImagem(){ return this.resIdImagem; }
}

Note o uso de um inteiro para armazenar a informação da imagem, o que nos


facilitará o trabalho mais para frente.
Muitas vezes não podemos ou não queremos criar uma nova classe para
exibir itens personalizados na lista. Nestas ocasiões você pode substituir a
criação de uma classe pelo uso de uma coleção Map, contendo chaves e
valores.
Considerando a coleção de cidades abaixo, que deve ser criada no topo da sua
Activity (e que poderia ser substituída por um SELECT no banco de dados ou
um GET em uma API remota, teremos todas nossas cidades carregadas em
memória. Note como defini o código da imagem usando a classe R e sua
propriedade estática drawable, que lista os ids únicos das imagens. Isso evita
ter de conhecer o caminho até o arquivo físico da imagem.
Código 47: disponível em luiztools.com.br/livro-android-fontes

cidades = new ArrayList<>();


cidades.add(new Cidade("Florianópolis", "Capital de Santa Catarina",
"SC", R.drawable.florianopolis));
cidades.add(new Cidade("Curitiba", "Capital do Paraná", "PR",
R.drawable.curitiba));
cidades.add(new Cidade("São Paulo", "Capital de São Paulo",
"SP",R.drawable.sao_paulo));
cidades.add(new Cidade("Porto Alegre", "Capital do Rio Grande do
Sul", "RS", R.drawable.porto_alegre));

Mas como o Android vai saber qual informação deve ir em cada parte do
nosso layout personalizado? Nós temos de dizer isso pra ele! O ArrayAdapter
tradicional não consegue dar conta deste recado, por isso devemos criar o
nosso Adapter personalizado, que vamos chamar de CidadeAdapter e que
será uma subclasse do Adapter original. Nesse Adapter personalizado,
colocaremos a lógica de como fazer o binding dos campos de cada item da
ListView conforme o layout XML que criamos para o mesmo. Segue abaixo
um exemplo onde o método getView será disparado uma vez para cada item
que adicionarmos na ListView, carregando a cidade em questão e
adicionando cada um dos seus atributos ao widget correspondente no layout
item_modelo.xml.
Código 48: disponível em luiztools.com.br/livro-android-fontes

public class CidadeAdapter extends ArrayAdapter<Cidade> {


private List<Cidade> items;

public CidadeAdapter(Context context, int textViewResourceId,


List<Cidade> items) {
super(context, textViewResourceId, items);
this.items = items;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null) {
Context ctx = getContext();
LayoutInflater vi =
(LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE
v = vi.inflate(R.layout.item_modelo, null);
}
Cidade cidade = items.get(position);
if (cidade != null) {
((TextView)
v.findViewById(R.id.lblCidade)).setText(cidade.getNome());
((TextView) v.findViewById(R.id.lblUF)).setText("/" +
cidade.getUf());
((TextView)
v.findViewById(R.id.lblDescricao)).setText(cidade.getDescricao());

((ImageView)
v.findViewById(R.id.imgCidade)).setImageResource(cidade.getIdImagem());
}
return v;
}
}

Agora devemos alterar o método que é disparado quando o nosso botão de


buscar é pressionado (btnBuscar_OnClick), para que carregue um
CidadeAdapter em nosso ListView, usando o nosso layout personalizado
item_modelo.xml. O código abaixo mostra exatamente isso, já com a lógica
de busca refatorada.
Código 49: disponível em luiztools.com.br/livro-android-fontes

public void btnBuscar_OnClick(View v){


String busca = txtPesquisa.getText().toString();
List<Cidade> encontradas = new ArrayList<>();
for(Cidade cidade : cidades){
if(cidade.getNome().contains(busca)) encontradas.add(cidade);
}
CidadeAdapter adapter = new CidadeAdapter(getBaseContext(),
R.layout.item_modelo, encontradas);
lstResultados.setAdapter(adapter);
}

Realizando o mesmo teste que fizemos com o app anterior, mas desta vez
com a nossa nova versão com objeto e layout personalizados, obtemos o
seguinte resultado:
E por fim, como mencionado anteriormente, no evento de click de um item
da lista, na hora que quisermos fazer alguma ação com base no objeto
selecionado, teremos de fazer uma conversão um pouco diferente do toString
usado anteriormente, como segue:
Código 50: disponível em luiztools.com.br/livro-android-fontes

lstResultados.setOnItemClickListener(new ListView.OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Cidade cidade = (Cidade)lstResultados.getItemAtPosition(position);
Toast.makeText(getBaseContext(), "Item " + cidade.getNome(),
Toast.LENGTH_LONG).show();
}
});

Existe um componente ainda mais moderno e com mais performance


chamado RecyclerView, disponível em versões mais novas do Android. Para
aprender como utilizar, sugiro este tutorial em meu blog:
https://www.luiztools.com.br/post/tutorial-crud-em-android-com-sqlite-e-
recyclerview/
ScrollView e HorizontalScrollView
ScrollViews são elementos extremamente úteis em aplicações para
dispositivos com touchscreen. É muito comum, principalmente em
smartphones menores, alguns elementos da interface ficarem escondidos
devido à diminuta resolução da tela. Com isso, temos por ímpeto realizar a
“rolagem” da tela com o dedo, para ver o restante das informações e
componentes visuais. O ScrollView te dá o poder de tornar seus layouts
“roláveis” com os dedos, sem nada de programação Java, apenas com XML.
Dentro de um layout XML qualquer (seja Relative ou Linear, não importa),
arraste um componente ScrollView (para scroll vertical) ou
HorizontalScrollView (para scroll horizontal) para dentro do layout antes
mesmo de colocar qualquer outro widget, obtendo o código abaixo.
Código 51: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="br.com.luiztools.controller.MainActivity">

<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/scrollView">
</ScrollView>

</RelativeLayout>

Dentro do componente ScrollView podemos adicionar APENAS UM


elemento visual, que geralmente é algum layout como RelativeLayout ou
LinearLayout. Segue exemplo:
Código 52: disponível em luiztools.com.br/livro-android-fontes

<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/scrollView">

<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</RelativeLayout>

</ScrollView>

Dentro deste RelativeLayout mais interno que adicionei, posso colocar


quantos elementos eu quiser, para montar a interface gráfica de que necessito
para minha aplicação, sem me preocupar que todos fiquem visíveis na tela do
usuário, pois caso ele não veja algum, poderá rolar a interface com o dedo até
encontrar os demais. Esse recurso é especialmente útil quando deixamos à
cargo do usuário a escolha de orientação para uso do dispositivo e ele opta
por paisagem, onde as dimensões da altura ficarão muito menores que o
padrão de desenvolvimento.
Existem apenas duas propriedades que valem ser mencionadas aqui:
● fillViewPort: defina como true para que seu ScrollView possa ocupar
toda a área visível da tela, em conjunto com os parâmetros
layout_width e height.
● scrollbars: você pode definir como horizontal, vertical ou none, para
permitir scrollbars nas respectivas orientações.
Outros containers que merecem ser citados mas que não há espaço no livro
para descrevê-los em detalhes são o GridView, ExpandableListView,
SearchView, TabHost, VideoView e DialerFilter. Todos possuem tutoriais
extremamente detalhados no site oficial: http://developer.android.com
Dialogs
Um dialog é uma pequena janela que alerta o usuário a tomar uma decisão ou
inserir informações adicionais. Um dialog não preenche a tela inteira e é
normalmente usado para eventos modais que requerem que o usuário tome
uma ação antes que ele possa continuar o que estava fazendo.

A classe Dialog é a classe base para dialogs, mas você deve evitar instanciar
Dialog diretamente. Ao invés disso, use uma das seguintes subclasses:
AlertDialog
Um dialog que pode mostrar um título, até três botões, uma lista de itens
selecionáveis ou um layout personalizado.
DatePickerDialog ou TimePickerDialog
Um dialog com uma interface pré-definida que permite ao usuário selecionar
data ou hora.
ProgressDialog
Android inclui outra classe dialog chamada ProgressDialog que exibe um
dialog com uma barra de progresso. Entretanto, se você precisa indicar o
carregamento ou progresso indeterminado, você deve ao invés disso usar uma
ProgressBar em seu layout, como indicam as guidelines do Android.
Usando Dialogs
Você pode criar, configurar e disparar um dialog da seguinte maneira:
Código 53: disponível em luiztools.com.br/livro-android-fontes

AlertDialog.Builder builder = new AlertDialog.Builder(this);


builder.setMessage("Mensagem da Janela")
.setTitle("Título da Janela");
AlertDialog dialog = builder.create();

No exemplo acima definimos uma mensagem (setMessage) e um título


(setTitle) para o nosso dialog, que será exibido assim que chamarmos
dialog.show().
Para adicionar botões (como nas janelas confirm do Javascript ou
MessageBox do Visual Basic), chame os métodos setPositiveButton() e
setNegativeButton():
Código 54: disponível em luiztools.com.br/livro-android-fontes

AlertDialog.Builder builder = new AlertDialog.Builder(this);


builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//usuário clicou em ok
}
})
.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//usuário clicou em Cancelar
}
});
AlertDialog dialog = builder.create();
Os métodos set...Button() exigem o texto do botão e um
DialogInterface.OnClickListener que define a ação que acontecerá quando o
usuário escolher este botão. Existem três diferentes botões que podem ser
adicionados:
Positive: usado para aceitar e continuar com a ação (OK).
Negative: usado para cancelar a ação.
Neutral: deve ser usado quando o usuário pode não querer continuar com a
ação, mas não quer necessariamente cancelar. Ele aparece entre os botões
positivo e negativo. Por exemplo, a ação pode ser “Lembre-me depois” .
Você pode adicionar somente um de cada botão em um AlertDialog.
Adicionando uma Lista
Existem três tipos de listas disponíveis com as APIs do AlertDialog:
● Lista tradicional de escolha única
● Lista de escolha única com radio buttons
● Lista de múltipla escolha com checkboxes
Para criar uma lista de escolha única como na figura abaixo, use o método
setItems():

Código 55: disponível em luiztools.com.br/livro-android-fontes


AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Título da Janela")
.setItems(R.array.cores, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//o parâmetro which diz o índice do item escolhido
}
});
AlertDialog dialog = builder.create();

Uma vez que a lista aparece na área do conteúdo do dialog, ele não pode
exibir uma lista e uma mensagem ao mesmo tempo e você deve definir um
título para o dialog com setTitle(). Para especificar itens para a lista chame
setItems(), passando um array. Alternativamente, você pode especificar uma
lista usando setAdapter(). Isto permite a você retornar à lista com dados
dinâmicos (como de uma base de dados) usando ListAdapter.
Abaixo o exemplo de array utilizado no código anterior, salvo em um arquivo
cores.xml na pasta res/values:
Código 56: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<resources>
<string-array name="cores">
<item>Azul</item>
<item>Branco</item>
<item>Amarelo</item>
</string-array>
</resources>

Por padrão, tocar um item da lista desfaz o dialog, a menos que esteja usando
os métodos setMultiChoiceItems (checkboxes) e setSingleChoiceItems (radio
buttons).
Criando um Dialog com layout personalizado
As classes que acabamos de ver definem o estilo e estrutura básica para seu
dialog, mas se quiser ir mais longe terá de usar um DialogFragment como
container. A classe DialogFragment fornece todos os controles que você
precisa para criar seu dialog e gerencia sua aparência, ao invés de chamar os
métodos do objeto Dialog.
Atenção: a classe DialogFragment foi originalmente criada com Android 3.0
(API 11) e mais tarde incorporada à Support Library para que fosse possível
ser utilizada em versões do Android 1.6+. Para usar a mesma versão deste
material, certifique-se de referenciar a biblioteca
android.support.v4.app.DialogFragment ao invés de
android.app.DialogFragment.
Logo abaixo está um AlertDialog personalizado básico, estendendo a classe
DialogFragment. Este exemplo foi retirado da documentação oficial do
Android e apenas usaremos o mesmo como um breve exemplo. Dentro do
evento onCreateDialog coloca-se a lógica de construção do nosso dialog
usando o design pattern Builder (se você ainda não conhece os design
patterns, deveria!), configurando a mensagem (com o método setMessage), a
ação do clique do botão “positivo” (setPositiveButton) e a ação do botão
“negativo” (setNegativeButton). Por último retornamos nosso diálogo criado
chamando o método create().
Código 57: disponível em luiztools.com.br/livro-android-fontes

public class FireMissilesDialogFragment extends DialogFragment {


@Override
public Dialog onCreateDialog(Bundle savedInstanceState){
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Fire missles?")
.setPositiveButton("Fire", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which) {
//FOGO!
}
})
.setNegativeButton("Cancel", new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//ABORTAR!
}
});
return builder.create();
}
}

Agora, quando criar uma instância desta classe e chamar o método show() em
um objeto do tipo FireMissilesDialogFragment e o dialog aparecerá como na
figura abaixo.
Código 58: disponível em luiztools.com.br/livro-android-fontes

public void pedirConfirmacao(){


DialogFragment fragment = new FireMissilesDialogFragment();
fragment.show(getFragmentManager(), "missiles");
}

Mas queremos ir mais longe que isso, certo? E se quisermos criar um dialog
onde o usuário possa digitar um texto em um EditText e a partir deste texto
realizarmos alguma ação? O primeiro passo é criarmos o layout desse dialog,
que chamei de dialog_nova_pasta e que está guardado junto com os demais,
na pasta res/layout, como segue:
Código 59: disponível em luiztools.com.br/livro-android-fontes
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Digite o nome da nova pasta:" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtNovaPasta" />
</LinearLayout>

O que deve se parecer com isso aqui (note que não coloquei os botões
propositalmente):

A seguir, vamos criar a classe do nosso diálogo personalizado, estendo a


original, DialogFragment e sobrescrevendo o evento onCreateDialog:
Código 60: disponível em luiztools.com.br/livro-android-fontes
public class NovaPastaDialog extends DialogFragment {

@Override
public Dialog onCreateDialog(Bundle bundle){
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Nova Pasta")
.setPositiveButton("Criar", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
//...
}
});
return builder.create();
}
}

Mas e o layout personalizado? Devemos carregar ele dentro do evento


onCreateDialog usando um inflator, como no exemplo abaixo, onde
carregamos uma View v com o inflator e depois passamos ela ao dialog
usando builder.setView:
Código 61: disponível em luiztools.com.br/livro-android-fontes

@Override
public Dialog onCreateDialog(Bundle bundle){
final Activity activity = getActivity();
LayoutInflater inflater = activity.getLayoutInflater();
View v = inflater.inflate(R.layout.dialog_nova_pasta, null);

AlertDialog.Builder builder = new AlertDialog.Builder(activity);


builder.setTitle("Nova Pasta")
.setView(v)
.setPositiveButton("Criar", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//...
}
});
return builder.create();
}

Para finalizar, agora basta programarmos o evento onClick do botão


“positivo”, que aqui neste exemplo recebeu o rótulo de “Salvar”. Neste
exemplo vamos ler o que o usuário informou no EditText chamado
txtNovaPasta para em seguida exibir um Toast com a palavra digitada.
Código 62: disponível em luiztools.com.br/livro-android-fontes

.setPositiveButton("Criar", new DialogInterface.OnClickListener() {


public void onClick(DialogInterface dialog, int which) {
EditText txtNovaPasta = (EditText)
((Dialog)dialog).findViewById(R.id.txtNovaPasta);
Toast.makeText(activity.getBaseContext(),
txtNovaPasta.getText().toString(), Toast.LENGTH_LONG).show();
}
});

Para disparar este nosso dialog personalizado, basta uma simples instanciação
de um objeto da classe NovaPastaDialog e a chamada ao método show(),
como segue:
Código 63: disponível em luiztools.com.br/livro-android-fontes

new NovaPastaDialog().show();

Como resultado teremos:


Caso quiséssemos adicionar e programar os demais botões, o processo é
semelhante, bastando usar os métodos setNegativeButton e setNeutralButton.
Caso quiséssemos outros botões ou ainda outros componentes, bastaria criar
o layout correspondente, definir os listeners, etc, etc. O céu é o limite quando
o assunto são dialogs personalizados!
Menus
Menus são componentes comuns de interfaces com o usuário em muitos tipos
de aplicações. Para fornecer uma experiência familiar e consistente ao
usuário, você deve usar as APIs de Menu para apresentar ações ao usuário e
outras opções em suas activities.
Veremos como utilizar dois tipos de menu: Menu de Opções e Menu
Contextual. Outros tópicos mais avançados em programação de menus
podem ser encontrados no artigo oficial:
http://developer.android.com/guide/topics/ui/menus.html .
Definindo um Menu em XML
Para todos os tipos de menu, Android fornece um formato XML padrão para
definir os itens do menu. Ao invés de construir um menu no código da sua
Activity (o que é perfeitamente possível), você deve definir um menu e todos
seus itens em um resource de menu XML. Você pode então carregar o
resource de menu em sua activity ou fragmento.
Usar um resource de menu é uma boa prática por algumas razões:
● É mais fácil visualizar a estrutura do menu em XML.
● Ele separa o conteúdo do menu do código comportamental da sua
aplicação.
● Ele permite criar configurações de menu alternativas para diferentes
versões de plataforma, tamanhos de tela e outras configurações.
Para definir o menu, crie um arquivo XML dentro do seu diretório res/menu/
e construa o menu com os seguintes elementos, que neste exemplo são para
um app de Gerenciador de Arquivos, com as opções Nova Pasta, Novo
Arquivo e Sair:
Código 64: disponível em luiztools.com.br/livro-android-fontes

<menu 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"
tools:context=".MainActivity">
<item android:id="@+id/menu_nova_pasta" android:title="Nova
Pasta"
android:orderInCategory="100" app:showAsAction="never" />
<item android:id="@+id/menu_novo_arquivo" android:title="Novo
Arquivo"
android:orderInCategory="200" app:showAsAction="never" />
<item android:id="@+id/menu_sair" android:title="Sair"
android:orderInCategory="300" app:showAsAction="never" />
</menu>

Para usar o menu em sua activity, você precisa carregar o resource de menu
(também chamado de “inflar”, que nada mais é do que converter um resource
XML em um objeto programável) usando MenuInflater.inflate(). Na seções
seguintes, você irá ver como inflar um menu de cada tipo.
Menu de Opções
Usamos options menu quando queremos incluir ações e outras opções que
são relevantes ao contexto da activity como “Buscar”, “Escrever e-mail” e
“Configurações”. Seus itens/opções aparecem na tela em um local que
depende da versão do Android:
● ao usar Android 2.3.x (API 10) ou anterior, as opções irão aparecer no
rodapé da tela quando usuário pressionar o botão de menu. Você pode
ter até 6 items no menu que serão visíveis e se adicionar um sétimo, o
Android cria automaticamente uma espécie de paginação no menu.
● Com Android 3.0 ou superior (API 11), os itens do menu estão
disponíveis na action bar e podem ser exibidos ao tocar o canto
superior direito (com o ícone de menu) ou pressionando o botão físico
de menu no dispositivo.
Para especificar um menu de opções para uma activity, sobrescreva o método
onCreateOptionsMenu(). Neste método, você pode inflar seu resource XML
de menu em um Menu fornecendo no callback. Por exemplo:
Código 65: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

Você pode também adicionar itens no menu usando o método add() e retornar
itens com findItem().
Eventos de Clique
Quando o usuário seleciona um item do options menu (incluindo itens de
ação na action bar), o sistema dispara o método onOptionsItemSelected() da
sua activity. Este método passa o MenuItem selecionado. Você pode
identificar o item chamando getItemId(), que retorna o ID único para o item
do menu (definido pelo atributo android:id no resource de menu ou por um
inteiro fornecido no método add() ). Você pode comparar este ID com
prováveis Ids conhecidos para executar a ação apropriada, como segue:
Código 66: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();

if (id == R.id.menu_nova_pasta) {
//ação de criar nova pasta
return true;
}
else if(id == R.id.menu_novo_arquivo){
//ação de criar novo arquivo
return true;
}
else if(id == R.id.menu_sair){
finish();
}

return super.onOptionsItemSelected(item);
}

Quando você consegue manipular um item do menu com sucesso, retorne


true. Se você não conseguiu manipular você deve chamar a implementação de
onOptionsItemSelected() da superclasse (que retornará false).
Menu Contextual
Um menu contextual oferece ações que afetam um item específico na
interface. Você pode fornecer um menu contextual para qualquer view, mas
ele é mais comumente usado com ListView, GridView ou outras view
collections em que o usuário possa querer efetuar uma ação direta sobre cada
item.
O tipo mais comum de Menu Contextual é o flutuante. Em um menu
contextual flutuante um menu aparece como uma lista flutuante de itens de
menu (parecida com um dialog) quando o usuário executa um clique-longo
em uma view que declara suporte a menus contextuais. Usuários podem
disparar uma ação contextual de um item de cada vez.
Para criar um menu contextual flutuante você deve começar registrando a
view que estará associada ao menu contextual chamando
registerForContextMenu() e passando a view.
Código 67: disponível em luiztools.com.br/livro-android-fontes

registerForContextMenu(lstArvore);

Se sua activity usa um ListView/GridView e você quer que cada item mostre
o mesmo menu contextual, registre todos itens para um menu contextual
passando a ListView/GridView para registerForContextMenu(), como
mostrado acima. Depois disso, implemente o método
onCreateContextMenu() na sua Activity.
Quando a view registrada recebe um evento de clique-longo, o sistema
dispara seu método onCreateContextMenu(). Aqui é onde você define seus
itens do menu, normalmente inflando um resource XML de menu, como
segue:
Código 68: disponível em luiztools.com.br/livro-android-fontes

@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
if (v.getId() == R.id.lstArvore) {
getMenuInflater().inflate(R.menu.menu_context, menu);
}
}

MenuInflater permite inflar o menu contextual a partir de um resource XML


de menu. Os parâmetros do método de callback incluem a view que o usuário
selecionou e um objeto ContextMenu.ContextMenuInfo que fornece
informações adicionais sobre o item selecionado. Se sua activity tem muitas
view e cada uma oferece um menu contextual diferente, você deve usar estes
parâmetros para determinar qual context menu inflar.
Em seguida implemente onContextItemSelected() para quando o usuário
selecionar um item do menu o sistema chame este método e execute a ação
apropriada:
Código 69: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();

int id = item.getItemId();
if(id == R.id.menu_ctx_editar){
//ação editar
String itemSelecionado =
lstArvore.getItemAtPosition(info.position).toString();
Toast.makeText(this, itemSelecionado, Toast.LENGTH_LONG).show();
return true;
}
else if(id == R.id.menu_ctx_excluir){
//ação excluir
return true;
}
return super.onContextItemSelected(item);
}

O métodogetItemId() consulta o ID do item de menu selecionado que você


deve associar a cada item do menu no XML usando o atributo android:id,
como visto anteriormente. Agora dentro da ação, para pegar o item que foi
clicado, pode-se pegar as informações do item de menu clicado com o
método getMenuInfo(), incluindo a posição do item que foi clicado, como
mostra no exemplo acima.
Quando você manipula um item do menu com sucesso, retorne true. Se você
não manipular o item, deve passar o mesmo para a implementação da
superclasse.
5 ENTENDENDO AS ACTIVITIES
Aplicações para dispositivos Android são nativamente separadas em, ao
menos, duas camadas: apresentação e aplicação. Na verdade, é um pouco
mais complexo que isso, mas por ora, basta saber que a criação das telas do
aplicativo (os layouts que vimos anteriormente) é feita de maneira separada e
independente da lógica da aplicação em si, evitando o famoso código
spaguetti, muito popular nos anos 90 em aplicações web desenvolvidas com
PHP e ASP. Em Android, a lógica da aplicação (ou camada de controle) fica
à cargo das Activities.
Mas o que é uma Activity?
Primeiramente entenda que não existe aplicativo Android sem uma ou mais
Activities. Mesmo os frameworks e plataformas que lhe prometem o
desenvolvimento de apps sem mexer com Java, por baixo dos panos disparam
Activities previamente desenvolvidas sem você ficar sabendo.
Uma Activity é um componente de uma aplicação que fornece a lógica de
interação para uma tela do seu app, visando fazer funcionar UMA tarefa da
sua aplicação, como discar um número, tirar uma foto, enviar um e-mail ou
ver um mapa. Cada Activity possui uma interface gráfica, definida em um
arquivo XML de layout que geralmente preenche completamente a tela do
seu app enquanto a Activity está rodando.
Um app Android usualmente consiste de múltiplas Activities que estão
fracamente acopladas umas às outras, ou seja, são independentes, inclusive
podendo interagir com Activities de outras aplicações instaladas no
dispositivo, sob limitações que trataremos mais tarde. Tipicamente, uma
Activity de seu app será definida como a Activity “main”, que é apresentada
ao usuário quando a aplicação é iniciada e essa Activity main é definida no
arquivo AndroidManifest.xml, que fica na pasta manifests do seu projeto,
como segue.
Código 70: disponível em luiztools.com.br/livro-android-fontes

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

As múltiplas Activities que são criadas durante o uso do seu dispositivo


Android são armazenadas na memória em formato de pilha, ou seja, a cada
nova Activity criada ela fica sobre as anteriores, que neste caso ficarão em
modo Stop. Caso a Activity atual seja destruída ou o botão Back do
dispositivo seja pressionado, a Activity imediatamente anterior (agora topo
da pilha) voltará ao estado de Active e continuará sua execução.
Criando uma Activity
A despeito da Activity padrão que é criado obrigatoriamente com a criação de
um novo projeto Android (e que geralmente usa o modelo “Empty Activity”
e possui o nome de MainActivity), criar uma Activity é muito fácil, bastando
clicar com o botão direito do mouse sobre o package do seu app e escolhendo
a opção New > Activity > Empty Activity que apenas irá criar uma classe
Java que extende de AppCompatActivity, que é uma classe base para novas
Activities.
A janela abaixo mostra a tela de criação de Activity, com as opções de
escolher o nome da Activity (por padrão mantenha sempre a palavra Activity
ao final do nome da atividade, como CadastroActivity, ListagemActivity,
etc), o nome do layout XML associado à ela, o título da Activity (que aparece
no topo do aplicativo), um checkbox indicando se esta é ou não a Activity
inicial do app (essa opção faz com que o Android Manifest seja modificado
para que esta Activity seja a Main/Launcher) e o nome do pacote da
aplicação.
A superclasse Activity (ou AppCompatActivity) define o padrão de
comportamento para todas Activities, o chamado ciclo de vida. Ao criar uma
Empty Activity no Android Studio será criado a classe corretamente
incluindo o eventos básico onCreate.
Código 71: disponível em luiztools.com.br/livro-android-fontes

public class MyActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
}
}
Eventos
Um evento nada mais é do que um método que não é chamado pela gente
durante a programação, mas sim pelo próprio sistema operacional Android
sob determinadas circunstâncias. O onCreate é o primeiro evento que vamos
estudar e ele está intimamente relacionado ao funcionamento geral da
Activity pois tudo começa com ele. Outros eventos importantes também são
vistos neste tópico, que juntos formam o ciclo de vida de uma Activity, ou
seja, manipulam os estados possíveis de uma Activity desde a sua criação até
sua finalização, como mostra a imagem a seguir:
Evento onCreate
Primeiro: o evento onCreate é chamado uma única vez quando a Activity é
carregada em memória (instanciada). Ou seja, é aqui que realizamos todos os
carregamentos únicos da nossa atividade, como layout que será utilizado
(através da chamada do método setContentView) e demais widgets que
quisermos manipular, como veremos mais adiante.
Código 72: disponível em luiztools.com.br/livro-android-fontes

public class MyActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
}
}

No exemplo acima, o evento onCreate faz uma chamada ao mesmo evento na


superclasse (chamada obrigatória) e em seguida define qual o layout que será
utilizada para renderização na tela do dispositivo. Note que não é possível
manipular nenhum widget antes de definir o layout XML que estamos usando
e note também que é possível ter mais de um XML para cada Activity, que
pode ser escolhido conforme a necessidade. Como exemplo posso citar a
criação de layouts XML diferentes para a orientação retrato e paisagem de
um app, caso sejam drasticamente diferentes.
No setContentView o parâmetro esperado em um número inteiro contendo o
identificador único de memória do layout que vamos utilizar. Como aqui
ninguém tem memória de elefante pra se lembrar de identificadores
numéricos de 32-bit o Android possui um mecanismo bem fácil de acessar
tais valores, através de uma classe gerada automaticamente chamada R, de
resource (recurso em inglês). A classe R, que fica em
app/build/generated/source/r/debug/packageName é gerada automaticamente
cada vez que nosso projeto é compilado e contém todas as referências de
memória de todos componentes de nosso app, incluindo layouts, widgets,
assets, etc, como mostra o código abaixo:
Código 73: disponível em luiztools.com.br/livro-android-fontes

public final class R {


public static final class attr {
}
public static final class dimen {
public static final int activity_horizontal_margin=0x7f040000;
public static final int activity_vertical_margin=0x7f040001;
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class id {
public static final int action_settings=0x7f080005;
public static final int btnCentro=0x7f080000;
public static final int btnLeste=0x7f080003;
public static final int btnNorte=0x7f080001;
public static final int btnOeste=0x7f080004;
public static final int btnSul=0x7f080002;
}
public static final class layout {
public static final int activity_my=0x7f030000;
}
public static final class menu {
public static final int my=0x7f070000;
}
public static final class string {
public static final int action_settings=0x7f050000;
public static final int app_name=0x7f050001;
}
public static final class style {
public static final int AppTheme=0x7f060000;
}
}

Esta é a classe R de um projeto meu, e ela costuma ficar bem grande no


decorrer do desenvolvimento. Através da classe R podemos acessar o id de
qualquer widget ou o nome de qualquer layout, chamando as propriedades
estáticas id e layout, respectivamente. Com isso, na chamada
R.layout.activity_my estamos passando ao setContentView o endereço de
memória onde se encontra o layout XML que queremos.
Após carregarmos o layout XML temos a nossa disposição o acesso aos
widgets presentes no mesmo. Ou quase. Na verdade ainda temos que fazer a
ligação manual entre uma variável local e a referência de memória do widget
(usando a classe R). O exemplo de código abaixo mostra como carregar um
campo de texto para uma variável local, a fim de alterar ou ler seu conteúdo.
Uma vez que fizemos o ‘binding’, a variável txtTeste é nossa ‘ponte’ com
esse elemento de interface e representa o mesmo em nosso código Java. Se
alterarmos a variável, a interface refletirá a alteração e vice-versa.
Código 74: disponível em luiztools.com.br/livro-android-fontes

EditText txtTeste = (EditText)findViewById(R.id.txtTeste);


String texto = txtTeste.getText().toString();
txtTeste.setText("Novo teste");

Note o uso do método findViewById, que assim como o setContentView,


espera o identificador de memória do componente a ser carregado, o qual
obtemos através do uso da classe R e sua propriedade estática id, seguido do
id do widget no arquivo XML, neste caso txtTeste. O método findViewById
retornar um objeto genérico e, por isso, realizamos a conversão para o tipo
correto usando a técnica de ‘casting’ ou conversão explícita (basta colocar o
tipo desejado entre parênteses, entre a variável e o método que retorna o
objeto).
Esse carregamento da variável do tipo EditText e de qualquer outro tipo
(TextView, Button, etc) pode ser realizado em qualquer ponto do código
Java, mas depois do setContentView ter carregado o layout XML, ou teremos
NullReferenceException. Uma prática comum no desenvolvimento Android é
carregar os elementos que serão manipulados via código para variáveis da
própria Activity, a fim de torná-las acessíveis em qualquer ponto da mesma.
O exemplo abaixo mostra o carregamento de todas variáveis de um app de
geolocalização logo no evento onCreate do app:
Código 75: disponível em luiztools.com.br/livro-android-fontes
public class MyActivity extends Activity {

EditText txtTeste;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);

txtTeste = (EditText)findViewById(R.id.txtTeste);
}
}

Evento onRestart
Como mencionado anteriormente, o evento onCreate somente é executado
uma vez, quando a Activity é criada. Entretanto, podemos querer executar
alguma(s) tarefa(s) toda vez que a Activity voltar a ser o centro das atenções
do usuário, ou seja, toda vez que uma Activity estiver no topo da pilha de
Activities do Android após ter sido deixada em segundo plano (ex: um
aplicativo que vai para uma Activity de tirar foto e depois volta para a
Activity de seleção de imagem). Nessas horas devemos usar o evento
onRestart, que deve ser sobrescrito da superclasse Activity.
Código 76: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onRestart(){
super.onRestart();
//faz algo ao reiniciar
}

Esse evento pode ser colocado em qualquer ponto da sua classe que extende
Activity (MyActivity.java, por exemplo) e obrigatoriamente deve ter como
primeira instrução a chamada ao onRestart da superclasse, sendo que logo
abaixo podemos colocar o código que quisermos.
Evento onCreateOptionsMenu e onOptionsItemSelected
Estes dois eventos estão intimamente relacionados com o processo de criação
de funcionamento dos menus contextuais do seu aplicativo. Tão logo sua
Activity é criada e o evento onCreate é disparado, temos também outro
evento que entra em ação, que é onCreateOptionsMenu, que cria as opções
que existirão no menu contextual desta Activity. Os menus foram melhor
explicados no capítulo anterior, sobre containers e sua leitura é recomendada
uma vez que os menus são itens recorrentes em apps.
Outros Eventos
O ciclo de vida de uma Activity Android é muito mais complexo do que
apenas três ou quatro eventos. A imagem abaixo mostra um pouco dos
callbacks mais comuns de serem utilizados para manipular os eventos que
podem ocorrer nativamente com uma Activity, independente dos controles
que existirem na mesma. Consulte a documentação oficial do Android em
developers.android.com para saber mais detalhes acerca do ciclo de vida de
activities Android.
Os eventos relacionados aos widgets (click do Button, select do RadioButton,
etc) foram tratados em detalhes nos tópicos correspondentes do capítulo
anterior e em nada afetam o comportamento do ciclo de vida da Activity.
Código 77: disponível em luiztools.com.br/livro-android-fontes

public class MyActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
//a activity está sendo criada
}

@Override
protected void onResume(){
super.onResume();
//a activity se tornou visível
}

@Override
protected void onPause(){
super.onPause();
//outra activity está assumindo o foco
}

@Override
protected void onStop(){
super.onStop();
//esta activity já não é mais visível
}

@Override
protected void onStart(){
super.onStart();
//a activity está sendo inicializada
}

@Override
protected void onDestroy(){
super.onDestroy();
//a activity está sendo finalizada
}
}
Iniciando e Finalizando Activities
Uma vez que é extremamente comum nossos apps terem mais de uma
Activity, é extremamente necessário que tenhamos o conhecimento de como
realizar as transições entre as mesmas. Para iniciar uma Activity usamos o
método startActivity, herdado da superclasse Activity, conforme abaixo.
Código 78: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onOptionsItemSelected(MenuItem item){
int id = item.getItemId();
if(id == R.id.action_settings){
Intent intent = new Intent(this, OutraActivity.class);
startActivity(intent);
}
return super.onOptionsItemSelected(item);
}

Neste exemplo, quando o usuário clicar no item do menu chamado settings,


iniciaremos uma Activity chamada OutraActivity. Não entendeu? Já vou
explicar.
Iniciando uma Activity
O sistema operacional Android não permite que uma Activity chame
qualquer recurso explicitamente, para garantir o máximo de segurança e
controle. Entre uma das exigências para se transitar de uma Activity para
outra está o registro de nossa intenção de troca de tela, que no Android
reproduz-se com o uso da classe Intent (intenção, literalmente). Devemos
criar primeiro um Intent onde dizemos qual a Activity que quer realizar a
ação e para qual Activity queremos ir com essa ação, simbolizados por this
(instância atual de activity) e OutraActivity.class, respectivamente. Somente
na sequência é que damos o start na Activity propriamente dita, passando o
Intent por parâmetro.
Código 79: disponível em luiztools.com.br/livro-android-fontes
Intent intent = new Intent(this, OutraActivity.class);
startActivity(intent);

Note que ao dispararmos o método startActivity, o evento onCreate da


respectiva Activity de destino será disparado e a mesma será empilhada sobre
a tela atual, que ficará em modo Stop, até que a nova Activity seja finalizada,
o botão de Back seja pressionado ou a aplicação inteira seja encerrada.
Finalizando uma Activity
Finalizar uma Activity é mais fácil do que criar uma e podemos fazê-lo
através do método finish(), herdado da superclasse Activity. Em um menu
contextual onde temos duas opções: “Nova Tarefa” e “Sair”, temos o
seguinte código Java para manipular o evento de seleção do item, onde
usamos o finish() para encerrar a Activity:
Código 80: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onOptionsItemSelected(MenuItem item){
int id = item.getItemId();
if(id == R.id.action_settings){
Intent intent = new Intent(this, OutraActivity.class);
startActivity(intent);
}else{
finish();
}
return super.onOptionsItemSelected(item);
}

Passando informações para uma Activity


Ás vezes precisamos passar informações de uma Activity para outra. Por
exemplo, em uma Activity de listagem de produtos podemos ter o
comportamento de, ao clicar em um nome na listagem, somos redirecionados
para uma Activity de detalhes, onde vemos todas informações de um produto.
Mas como saberemos qual produto foi clicado na Activity anterior? Usando
Extras!
Código 81: disponível em luiztools.com.br/livro-android-fontes

Intent intent = new Intent(this, OutraActivity.class);


intent.putExtra("ItemSelecionado", 1);
startActivity(intent);

Neste exemplo adiciono um Extra chamado “ItemSelecionado” e atribuo a ele


um valor arbitrário de ‘1’, sendo que a chamada ao putExtra pode receber
diversos tipos diferentes de valores, incluindo qualquer objeto de qualquer
classe que implemente a interface Serializable do Java (caso não entenda
exatamente como funciona serialização, apenas adicione a instrução
implements Serializable na classe que deseja passar por parâmetro que irá
funcionar). Eu posso colocar tantos extras quanto necessário, apenas
realizando sucessivas chamadas ao putExtra, para na sequência mandar
iniciar a respectiva Activity.
Mas como capturo esta informação na Activity de destino?
Código 82: disponível em luiztools.com.br/livro-android-fontes

Intent intent = getIntent();


int itemSelecionado = intent.getIntExtra("ItemSelecionado", 0);

Usando o método herdado getIntent() temos acesso ao Intent que originou a


chamada à esta Activity, e o objeto Intent retornado nos dá acesso aos seus
extras através de uma série de métodos como getIntExtra,
getBooleanExtra,getSerializableExtra(para pegar objetos passados pelo
Intent), etc. Nestes métodos, devemos passar o nome do extra que queremos
retornar (tal qual definido no Intent) e o valor default do extra caso o mesmo
não seja encontrado (i.e. esteja null). No caso de objetos serializáveis, você
ainda deverá fazer o casting (conversão) para o tipo correto antes de poder
voltar à usar o objeto completamente.
Pense nesse procedimento de troca de mensagens entre as Activities como a
QueryString de aplicações web e não abuse do mesmo colocando objetos
grandes e/ou complexos para não sobrecarregar sua Activity.
6 ACESSO À DADOS LOCAIS
A persistência de dados, na computação, refere-se ao armazenamento não-
volátil de dados, por exemplo, o armazenamento em um dispositivo físico
como um disco rígido. Quando se grava um arquivo no disco, por exemplo, o
dado está sendo "eternizado", ou seja, deixa de ficar volátil na memória RAM
e passa a ser escrito num dispositivo que armazena a informação de modo
que ela não desapareça facilmente.
Pode-se dizer que de maneira geral, o termo persistência é associado a uma
ação que consiste em manter em meio físico recuperável, como banco de
dados ou arquivo, de modo a garantir a permanência das informações de um
determinado estado de um objeto lógico.
Em Android existem diversos tipos de fontes de dados locais:
● APIs de apps instalados no smartphone
● Arquivos na memória flash ou cartão SD
● Banco de dados local
Como as APIs de apps instalados no smartphone possuem as mais diversas
formas de acesso, vamos nos focar nas duas mais comuns: arquivos na
memória flash ou cartão SD e banco de dados local. Ambas possuem
vantagens e desvantagens e entendê-las nos ajuda a escolher a melhor opção
de acordo com a situação.
Arquivos
Os dispositivos com o Android permitem a escrita e leitura de arquivos na
memória interna (flash) e no cartão SD, independente da configuração do
hardware do dispositivo. Uma vez salvo o arquivo no dispositivo, torna-se
fácil anexá-lo em um e-mail, enviá-lo via Bluetooth ou até copiá-lo através de
um cabo USB.
Como cada aplicação do Android roda em seu próprio contexto, um arquivo
salvo na memória interna só pode ser manipulado pela própria aplicação que
criou o arquivo. O arquivo não é visualizado por outras aplicações e não pode
ser manipulado pelo usuário.
Muitas vezes é necessário lidar com arquivos como repositório de dados. Em
alguns casos não temos como usar um banco de dados ou simplesmente
precisamos passar um conjunto de informações estruturados de uma ponta à
outra, pela Internet, por exemplo. Nestes casos, você terá de utilizar alguma
estrutura para os seus arquivos, a fim de permitir que ele seja lido
corretamente.
O processo de transformar objetos da aplicação em dados que podem ser
escritos e transferidos via arquivos é o que chamamos de serialização. O
processo contrário, de transformar dados de arquivos novamente em objetos
da aplicação (neste caso em objetos Java) é o que chamamos de de-
serialização. Quando trabalhamos com estas operações é muito importante
que exista um padrão, um formato de arquivo bem definido que será
utilizado, para que o processo seja de via dupla. Caso contrário, o seu arquivo
pode até conter os dados que deseja, mas ninguém irá conseguir decifrá-lo
depois.
Existem diversos formatos de arquivos possíveis, sendo que abaixo listo
alguns deles e entro em mais detalhes sobre o formato JSON, muito utilizado
nos últimos anos.
Flat-File
Flat-File é a denominação para arquivos que possuem um layout próprio de
guardar os dados dentro dele. Geralmente armazenam cada registro em uma
linha de um arquivo de texto (note que a extensão não influencia, apenas o
conteúdo do arquivo), definindo um número de colunas para cada valor que
deve ser armazenado ou um separador de colunas, como ^ ou |
A vantagem de trabalhar com Flat-Files é que o tamanho do arquivo
geralmente é menor que os outros padrões e a desvantagem é que eles não
são intercambiáveis, ou seja, somente a empresa que criou o layout do
arquivo que consegue entendê-lo, pois a olho nu não fazem muito sentido.
Ex: arquivos de remessa e retorno de cobrança dos bancos
Comma Separated Value (CSV)
Mais conhecido como CSV, devido à sua extensão, é um arquivo que simula
uma planilha de dados, assim como o Excel, possuindo linhas e colunas.
Possui um registro por linha do arquivo de texto e cada “coluna” da pseudo-
tabela é separado por por algum caracter, geralmente vírgulas (como o
próprio nome sugere) mas também é comum serem vistos com ponto-e-
vírgula (padrão Windows). Estes arquivos podem ou não ter cabeçalho, que é
uma linha com o nome de cada coluna, ajudando no entendimento do mesmo.
A vantagem de trabalhar com CSV é que o formato do mesmo é pequeno se
comparado à padrões mais modernos como XML ou JSON, e que pode ser
aberto no Excel, para posterior análise. A desvantagem é que não é um
formato flexível, e uma vez definido o padrão do CSV no seu cabeçalho,
todas as linhas devem segui-lo.
Ex: arquivos de Ips por país, exportação de dados para leitura humana no
Excel
eXtensible Markup Language (XML)
http://www.xml.org
Mais conhecidos como XML, devido à sua extensão de arquivo, é um
formato extensível de arquivo que permite a cada empresa criar o padrão de
arquivo XML que necessitar para seu sistema. Algumas regras básicas
sempre devem ser seguidas, como sua organização em tags (assim como no
HTML), o fato da primeira tag ter de ser o cabeçalho XML com o encoding
(codificação) do arquivo (geralmente UTF-8) e existindo ainda a
possibilidade de se usar um arquivo de estrutura (schema) como o XSD para
validar o arquivo de dados XML.
XML são largamente utilizados em sistemas desde a sua invenção, seja
devido à sua capacidade de armazenar diferentes tipos de dados em um
mesmo arquivo, sem confusão, sua característica hierárquica em nós (assim
como uma árvore) que permite buscas e parsing muito rápidos e a sua fácil
leitura a olho nú, sem uso de software. É comum as empresas
disponibilizarem guias de integração com seus sistemas definindo padrões de
XML e considerando seus benefícios, é um formato que vale a pena ser
aprendido.
A própria plataforma Android usa arquivos XML para decsrição das
interfaces gráficas de seus aplicativos, itens de menu e spinners, textos
estáticos da aplicação, configuração e muito mais, como você já deve ter
percebido.
Ex: feeds RSS, sites XHTML, arquivos de configuração
Javascript Simple Object Notation (JSON)
http://www.json.org
Mais conhecido pela sua abreviação JSON, é um formato criado inicialmente
para a web mas que logo se tornou muito utilizado para transmissão rápida e
dinâmica de dados pela Internet. JSON é extremamente flexível a ponto de
permitir arquivos tão pequenos quanto CSV e menores que XML. Mas não
pense que eles são “bagunçados”, um JSON pode ser tão estruturado quanto
um XML, com a vantagem de gerar um arquivo muito menor devido ao
problema de repetição de tags do XML.
Pode-se representar qualquer estrutura de dados em um arquivo JSON, uma
vez que são orientados a objetos, e não somente tabelas como os formatos de
arquivos em geral. Desta maneira, podemos criar arrays, objetos com tipos
bem definidos, associação entre objetos (como agregação e composição),
campos opcionais e assim por diante.
JSON é largamente utilizado na web e principalmente em APIs de integração
com sites, como no caso do Facebook e Twitter. Devido à sua popularidade
existem diversas implementações de bibliotecas que fazem o encode (escrita)
e decode (leitura) de dados neste formato pra todas as linguagens modernas e
muitas linguagens antigas também.
Em Android, já possuímos suporte nativo à serialização e deserialização com
JSON através do package org.json, onde basicamente usamos as classes
JSONObject para representar objetos e JSONArray para representar coleções.
Exploraremos estas classes e a utilidade de JSON no capítulo sobre acesso à
dados externos, uma vez que JSON é um padrão muito utilizado em APIs
web.
Exemplo: Gerenciador de Arquivos
Em nosso exemplo prático de manipulação de arquivos vamos criar um
Gerenciador de Arquivos, bem básico, onde será possível listar, editar e
excluir pastas e arquivos de texto do cartão SD do dispositivo móvel. Toda a
parte de interface gráfica será realizada usando os conhecimentos
previamente adquiridos, e serão rapidamente mencionados nesta seção, cujo
foco é a manipulação dos arquivos em si.
Crie um novo projeto dando a ele o nome de Gerenciador de Arquivos. Nosso
aplicativo terá as seguintes atividades: uma listagem dos arquivos existentes
com os botões de ação, que chamaremos de MainActivity, e uma tela de
cadastro/edição do arquivo, que chamaremos de FormActivity, lembrando
que iremos manipular somente arquivos de texto.
Para criarmos o layout da MainActivity conforme imagem a acima,
precisamos usar um LinearLayout como “plano de fundo” com orientação
vertical. Os widgets da imagem são, de cima para baixo: um TextView
(Large), um ListView e dois Buttons (que estão dentro de um LinearLayout
horizontal), um rotulado como Nova Pasta e outro como Novo Arquivo. As
demais interações do Gerenciador de Arquivos se darão por meios de toques
nos itens da lista e o TextView no topo serve para mostrar o caminho de
diretório atual (chamado path).
O XML na próxima página ilustra o arquivo criado para esta interface. Note
como foi utilizado o conceito de layout_weight, onde definimos um “peso”
para aquele componente, a fim de manter a sua proporcionalidade entre as
diferentes resoluções de tela. Note também como foi aninhado outro
LinearLayout dentro do primeiro, a fim de alinhar os botões lado a lado.
Código 83: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<TextView
android:layout_width="fill_parent"
android:layout_height="50dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="/"
android:id="@+id/txtPath"
android:textStyle="bold"/>

<ListView
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="8"
android:id="@+id/lstArvore" />

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:layout_marginTop="20dp"
android:orientation="horizontal">

<Button
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="Nova Pasta"
android:onClick="btnNovaPasta_OnClick"
android:id="@+id/btnNovaPasta" />

<Button
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:onClick="btnNovoArquivo_OnClick"
android:text="Novo Arquivo"
android:id="@+id/btnNovoArquivo" />

</LinearLayout>
</LinearLayout>

Para programarmos esta tela devemos ir à MainActivity.java, onde já temos o


evento onCreate e os eventos de menu. Aqui, devemos realizar as bindings
dos widgets para variáveis locais e criar os métodos que tratarão os eventos
de clique dos botões. A figura abaixo mostra esta etapa da programação. Note
que não precisamos ligar os botões, mas apenas seus eventos.
Código 84: disponível em luiztools.com.br/livro-android-fontes

public class MainActivity extends ActionBarActivity {

TextView txtPath;
ListView lstArvore;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

txtPath = (TextView)findViewById(R.id.txtPath);
lstArvore = (ListView)findViewById(R.id.lstArvore);
}

public void btnNovoArquivo_OnClick(View v){


//clique no botão de novo arquivo
}

public void btnNovaPasta_OnClick(View v){


//clique no botão de nova pasta
}
}

A ideia é que nossa ListView exiba as pastas e arquivos presentes no cartão


SD, para isso vamos programar um método de carregamento destas
informações, como na imagem seguinte, que deve ser colocado em nossa
MainActivity.
Código 85: disponível em luiztools.com.br/livro-android-fontes

void atualizarArvore(){
String raiz = Environment.getExternalStorageDirectory().getPath();
String path = txtPath.getText().toString();
File file = new File (raiz + path);
String[] tree = file.list();

if(tree != null) {
ArrayList<String> arr = new ArrayList<>(Arrays.asList(tree));
Collections.sort(arr);
if(!path.equals("/")){
arr.add(0, "..");//se não estamos na raiz, deve dar esta opção para
voltar
}
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, arr);
lstArvore.setAdapter(adapter);
}
}

Começamos com Environment.getExternalStorageDirectory() que retorna o


objeto que representa o caminho para o armazenamento externo, se houver,
ou interno, em último caso. Com o path em mãos podemos instanciar um
objeto File que possui o método list() que retorna um array de Strings
contendo todas as pastas e arquivos da raiz do SD. Ordenamos estes objetos
com Collections.sort após transformá-lo em um ArrayList para depois
adicionar em um ArrayAdapter e por fim em nossa lstArvore, exibindo tudo
na tela.
A classe Environment, estática, nos traz uma série de métodos de uso geral
para pegar informações do Android. Entre elas, as informações do sistema de
arquivos como no uso do getExternalStorageDirectory que permite não
somente pegar informações da raiz do armazenamento externo como de
pastas pré-definidas como PICTURES, DOWNLOADS, etc. No exemplo
acima usamos apenas para pegar a raiz mesmo, concatenando com o texto
presente no TextView do topo da tela. Com essa informação criamos um
objeto do tipo File, que apesar do nome pode ser tanto um arquivo quanto
uma pasta.
Também vale salientar o trecho de código em que adiciono uma String “..” ao
topo da árvore de diretórios, que significa tipicamente “voltar”, e que
programaremos mais tarde como uma opção do usuário retornar ao diretório
anterior.
Para que toda vez que a nossa árvore de pastas e arquivos apareça ela esteja
atualizada, vamos programar o evento onWindowsFocusChanged para
disparar nosso método atualizarArvore.
Código 86: disponível em luiztools.com.br/livro-android-fontes

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
atualizarArvore();
}

Para que a leitura e escrita de arquivos funcione no armazenamento externo,


será necessário adicionar as duas permissões abaixo no
AndroidManifest.xml:
Código 87: disponível em luiztools.com.br/livro-android-fontes

<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
/>

O resultado pode ser visto na imagem abaixo, onde é possível ver a listagem
de pastas presentes na raiz do armazenamento externo, que pode ser tanto o
cartão SD, se presente, quanto o armazenamento interno (internal storage).
Nosso próximo passo agora é programar o clique de um item da lista. Caso
este item seja uma pasta, a lista deve ser atualizada com o conteúdo da
mesma. Caso o item seja um arquivo de texto, a FormActivity deve ser aberta
com o conteúdo do arquivo (neste caso apenas passamos o caminho do
arquivo e um flag edit=true, a FormActivity é quem se encarregará da lógica
restante, como veremos mais para frente). E por fim, caso o arquivo não seja
de texto, devemos apenas exibir uma mensagem de arquivo não suportado.
Para programar esta lógica, temos de mapear o gatilho OnItemClickListener
em nossa lstArvore (ListView), como abaixo, ainda dentro do nosso evento
onCreate.
Código 88: disponível em luiztools.com.br/livro-android-fontes

lstArvore = (ListView)findViewById(R.id.lstArvore);
lstArvore.setOnItemClickListener(new ListView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
String path = txtPath.getText().toString();
String item = lstArvore.getItemAtPosition(position).toString();
if(!item.equals("..")) {//se não é o item 'voltar'
File file = new File(Environment.getExternalStorageDirectory()+ path
+ "/" + item);
if (file.isDirectory()) {//se é uma pasta
txtPath.setText(path.endsWith("/") ? path + item : path + "/" +
item);
} else if (file.getName().endsWith(".txt")) {//se é um arquivo de texto
Intent intent = new Intent(MainActivity.this, FormActivity.class);
intent.putExtra("path", file.getAbsolutePath());
intent.putExtra("edit", true);
startActivity(intent);
} else {//outros arquivos
Toast.makeText(view.getContext(), "Arquivo não suportado!",
Toast.LENGTH_LONG).show();
}
}
else{
String[] partes = path.split("/");
String abovePath = path.replace(partes[partes.length-1], "");
txtPath.setText(abovePath);
}
atualizarArvore();
}
});

Note que existe uma bifurcação principal baseado no texto do item clicado,
logo no início do código do evento OnItemClickListener: caso o item seja
“..” devemos reescrever o path na TextView do topo da tela, para que quando
ocorrer a próxima atualização da listagem (atualizarArvore()) dê a impressão
de que voltamos ao diretório anterior. Caso contrário, seguem as condições
anteriormente comentadas, alterando o txtPath no caso de uma pasta (o que
forçará a entrar neste diretório na próxima atualização da listagem),
disparando a FormActivity no caso de um arquivo de texto (note como foi
passado o caminho absoluto do arquivo como Extra) ou apenas exibindo um
Toast de arquivo não suportado nos demais casos.
Agora vamos programar o botão de criar Nova Pasta. Como a criação de uma
pasta é muito simples, bastando dar um nome à mesma, ao invés de abrir uma
nova Activity para criar a pasta vamos apenas exibir um Dialog contendo um
campo de texto e um botão de Salvar.
Conforme visto anteriormente, no capítulo de Interface na seção de
Containers, podemos criar caixas de diálogo personalizadas, visando tornar
nossas Activities mais dinâmicas e menos numerosas. Para isso, devemos
criar nossa própria classe de Dialog estendendo a base DialogFragment,
como mostrado no exemplo abaixo, onde crio uma classe para a nossa caixa
de diálogo de criar nova pasta.
Código 89: disponível em luiztools.com.br/livro-android-fontes

public class NovaPastaDialog extends DialogFragment {

static String path;

@SuppressLint("ValidFragment")
public NovaPastaDialog(String path){
this();
this.path = path;
}

public NovaPastaDialog(){ super(); }

@Override
public Dialog onCreateDialog(Bundle bundle){
final Activity activity = getActivity();
LayoutInflater inflater = activity.getLayoutInflater();
View v = inflater.inflate(R.layout.dialog_nova_pasta, null);

AlertDialog.Builder builder = new AlertDialog.Builder(activity);


builder.setTitle("Nova Pasta")
.setView(v)
.setPositiveButton("Criar", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
EditText txtNovaPasta = (EditText)
((Dialog)dialog).findViewById(R.id.txtNovaPasta);
String novaPasta = NovaPastaDialog.path + "/" +
txtNovaPasta.getText();
if (new File(novaPasta).mkdir()) {
Toast.makeText(activity.getBaseContext(), "Pasta criada
com sucesso!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(activity.getBaseContext(), "Não foi
possível criar a pasta!", Toast.LENGTH_LONG).show();
}
}
});
return builder.create();
}
}

Não dê bola para o erro grafado no exemplo anterior, ele diz respeito a não
ser recomendável criar um construtor que não seja vazio em um
DialogFragment. O layout utilizado por este dialog é bem simples e pode ser
conferido logo abaixo:
Código 90: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Digite o nome da nova pasta:" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtNovaPasta" />
</LinearLayout>

Não vou entrar em detalhes da implementação do Dialog, embora o código


fonte anteriormente exibido esteja completo, uma vez que já vimos Dialogs
na seção de Containers. Basicamente no clique do botão positivo do Dialog
criamos uma instância da classe File apontando para a pasta que desejamos
criar e chamamos o método mkdir() que criará o diretório retornando true em
caso de sucesso ou false em caso de falha. Outra alternativa seria chamar o
método mkdirs() que cria não somente a pasta que queremos como também
todas as anteriores para chegar até ela.
Já o nosso botão de “Novo Arquivo” é mais simples, uma vez que apenas
direciona o usuário para a tela de criação/edição de arquivo passando o
caminho da pasta e informando que não é uma edição (edit=false).
Código 91: disponível em luiztools.com.br/livro-android-fontes

public void btnNovoArquivo_OnClick(View v){


Intent intent = new Intent(this, FormActivity.class);
intent.putExtra("path", txtPath.getText().toString());
intent.putExtra("edit", false);
startActivity(intent);
}
Lembrando que o nome deste método foi definido na propriedade onClick do
nosso Button com id de btnNovoArquivo. Resumindo: caso você clique em
um item da listagem que seja um arquivo você deverá abrir a FormActivity já
com os dados do arquivo de texto preenchidos (basicamente caminho, nome e
conteúdo) e caso clique neste botão, a FormActivity deve abrir exibindo
apenas o caminho até o arquivo que será criado.
Vamos começar nossa FormActivity? Primeiro o layout, que basicamente é
um TextView com o caminho de pastas (path), depois um EditText para o
nome do arquivo e por fim um EditText com multiline para o conteúdo, além
de dois botões óbvios: “Salvar” e “Cancelar”.
Código 92: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"

tools:context="com.example.fernando.gerenciadordearquivos.FormActivity"

<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/txtPath"
android:text="Pasta:"/>

<EditText
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="Novo Arquivo.txt"
android:hint="Nome do Arquivo"
android:id="@+id/txtNomeArquivo"/>

<EditText
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="7"
android:text="Lorem ipsum dolor sit amet"
android:id="@+id/txtConteudoArquivo"/>

<LinearLayout
android:layout_width="fill_parent"
android:orientation="horizontal"
android:layout_height="0dp"
android:layout_weight="1">

<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Cancelar"
android:onClick="btnCancelar_OnClick"/>

<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Salvar"
android:onClick="btnSalvar_OnClick"/>

</LinearLayout>
</LinearLayout>

Agora o código da nossa Activity, que basicamente se divide entre o evento


onCreate, logo abaixo…
Código 93: disponível em luiztools.com.br/livro-android-fontes
TextView txtPath;
EditText txtNomeArquivo, txtConteudoArquivo;
boolean edit;
String path;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_form);

Intent intent = getIntent();


edit = intent.getBooleanExtra("edit", false);
path = intent.getStringExtra("path");

txtPath = (TextView)findViewById(R.id.txtPath);
txtNomeArquivo = (EditText)findViewById(R.id.txtNomeArquivo);
txtConteudoArquivo =
(EditText)findViewById(R.id.txtConteudoArquivo);

if(edit){
String[] partes = path.split("/");
String arquivo = partes[partes.length-1];
txtNomeArquivo.setText(arquivo);
txtPath.setText(path.replace(arquivo,""));
txtConteudoArquivo.setText(lerArquivo(path));
}else{
txtPath.setText(path);
}
}

...um método lerArquivo, que é utilizado para carregar o conteúdo do arquivo


(caso seja uma edição) na tela:
Código 94: disponível em luiztools.com.br/livro-android-fontes

String lerArquivo(String path){


File file = new File(Environment.getExternalStorageDirectory().getPath()
+ path);
StringBuilder text = new StringBuilder();

try {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;

while ((line = br.readLine()) != null) {


text.append(line + "\n");
}
br.close();
}
catch (IOException e) {
Toast.makeText(this, "Não foi possível ler o arquivo!",
Toast.LENGTH_LONG).show();
}
return text.toString();
}

...e os eventos dos cliques dos botões, extremamente simples. Apenas preste
atenção ao evento que cria o arquivo de texto onde chamamos o método
createNewFile (somente se o arquivo não existir, o que sabemos a partir da
chamada de file.exists()) a partir de um objeto do tipo File e depois usamos
uma série de classes da biblioteca java.io para colocar o texto dentro do
arquivo (FileOutputStream e OutputStreamWriter basicamente).
Código 95: disponível em luiztools.com.br/livro-android-fontes

public void btnSalvar_OnClick(View v){


try {
String arquivo = "/" + txtPath.getText() + "/" +
txtNomeArquivo.getText();
File myFile = new
File(Environment.getExternalStorageDirectory().getPath() + arquivo);
if(!myFile.exists())
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile);
fOut.write(txtConteudoArquivo.getText().toString().getBytes());
fOut.close();
finish();
} catch (Exception e) {
Toast.makeText(this, "Não foi possível salvar o arquivo!",
Toast.LENGTH_SHORT).show();
}
}

public void btnCancelar_OnClick(View v){


finish();
}

Esta lógica de salvamento contempla tanto a criação de um novo arquivo


quanto a edição de um já existente, uma vez que despeja todo o texto presente
o EditText multiline dentro do arquivo que pode estar vazio ou não.
Atenção: a partir do Android 6 Marshmallow (API23) o mecanismo de
permissões mudou consideravelmente e com isso erros como “open failed:
EACCES (Permission denied)” podem aparecer mesmo você solicitando a
permissão correta no manifesto. Para resolver, use o código abaixo que
solicita a permissão no momento em que precisar dela:
Código 96: disponível em luiztools.com.br/livro-android-fontes

private static final int REQUEST_EXTERNAL_STORAGE = 1;


private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions( activity, PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE );
}
}
O resultado podemos ver na imagem abaixo.

E o que falta agora? Bom, ainda temos de colocar a opção de excluir pastas e
arquivos certos? Esse tipo de opção geralmente costuma ficar associada à
ação de pressionar um item da lista durante algum tempo para exibição de
opções adicionais, em um menu contextual. A criação de menus contextuais
foi mostrada anteriormente na seção de containers, então vamos repassar
rapidamente os passos necessários.
O primeiro passo, considerando que será um menu com opções estáticas, é
criar o arquivo XML de menu na pasta res/menu, que eu vou chamar de
menu_context.xml e colocar apenas dois itens: “Editar Arquivo” e “Excluir
Arquivo”, conforme mostra o XML de exemplo abaixo:
Código 97: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_ctx_editar"
android:title="Editar Arquivo" />
<item
android:id="@+id/menu_ctx_excluir"
android:title="Excluir Arquivo" />
</menu>

Agora na nossa activity onde tem a listagem, devemos sobrescrever o método


onCreateContextMenu, conforme abaixo, carregando nosso XML (isso na
classe MainActivity).
Código 98: disponível em luiztools.com.br/livro-android-fontes

@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
if (v.getId() == R.id.lstArvore) {
getMenuInflater().inflate(R.menu.menu_context, menu);
}
}

Para que este método seja carregado quando um item da nossa lista for
pressionado durante alguns instantes (clique longo), devemos registrar esse
menu contextual para nossa ListView, dentro do método onCreate da
MainActivity:
Código 99: disponível em luiztools.com.br/livro-android-fontes

registerForContextMenu(lstArvore);
atualizarArvore();
}

...e para lidar com o evento gerado pelos itens do menu contextual devemos
sobrescrever o método onContextItemSelected, conforme a imagem na
próxima página. O resultado é que, quando realizarmos um clique longo
sobre algum item da lista, aparecerá um menu contextual com duas opções:
caso escolha Editar, você será direcionado para a tela de Edição de Arquivo,
caso escolha Excluir será exibido um dialog de confirmação, e caso opte por
OK o arquivo/pasta será excluído usando o método delete presenta na classe
File, que neste caso pode apontar tanto para um arquivo quanto para um
diretório. Em caso da exclusão a lista será atualizada devido ao evento
onWindowsFocusChanged que programamos anteriormente que será ativado
quando o foco sair do dialog e voltar à ListView.
Atenção: o método delete() somente exclui arquivos e pastas vazias. Caso
deseje excluir uma pasta com conteúdo, deverá excluir seu conteúdo
primeiro.
Visualmente esse menu contextual deve parecer com a imagem a seguir.
Existem diversas melhorias que poderíamos fazer para tornar este
gerenciador de arquivos mais completos. Aplicando a mesma técnica de itens
personalizados que vimos no tópico sobre ListView na seção de containers
podíamos colocar ícones de pastas e arquivos ao lado esquerdo do nome dos
mesmos. Talvez poderíamos colocar um ícone de um lápis ao lado direito dos
arquivos de texto, assinalando que estes são editáveis. Também poderíamos
programar a visualização de arquivos de imagem, apenas exibindo-os em
uma ImageView em outra activity. As possibilidades são inúmeras e acredito
que você tenha conseguido pegar a ideia de como prosseguir.
Apenas recapitulando os métodos da classe File que usamos neste exemplo:
● createNewFile: cria um novo arquivo com o caminho/nome
especificado;
● mkdir e mkdirs: cria a(s) pasta(s) no caminho especificado;
● delete: exclui o arquivo ou pasta (apenas vazias);
● list: retorna uma coleção de Strings contendo o “conteúdo” de uma
pasta;
Código 100: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();

final String itemSelecionado =


lstArvore.getItemAtPosition(info.position).toString();
final String raiz = Environment.getExternalStorageDirectory().getPath();
final String path = raiz + txtPath.getText().toString() + "/" +
itemSelecionado;

int id = item.getItemId();
if(id == R.id.menu_ctx_editar){
Intent intent = new Intent(MainActivity.this, FormActivity.class);
intent.putExtra("path", path);
intent.putExtra("edit", true);
startActivity(intent);
return true;
}
else if(id == R.id.menu_ctx_excluir){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Excluir")
.setMessage("Tem certeza que deseja excluir " + itemSelecionado
+ "?")
.setPositiveButton("Sim", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which) {
if(new File(path).delete()){
Toast.makeText(getBaseContext(), itemSelecionado + "
excluído com sucesso!", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getBaseContext(), "Não foi possível
excluir " + itemSelecionado + "!", Toast.LENGTH_LONG).show();
}
}
})
.create()
.show();

return true;
}
return super.onContextItemSelected(item);
}
Banco de Dados
Em Android, o banco de dados local que é utilizado é o SQLite, assim como
no iOS da Apple e diversas outras plataformas móveis e embarcadas. Quando
falamos de banco de dados móvel temos que ter em mente que o intuito não é
ter cópias de bancos de dados empresas rodando na palma de sua mão.
Bancos de dados de apps corporativos geralmente armazenam informações de
apoio ao sistema central, geralmente um ERP ou um CRM, e não são uma
cópia dos originais. Vamos dizer que eles servem mais como cache local para
uso offline do app, e não substituem o uso do sistema tradicional.
Com isso em mente, você notará e entenderá melhor que as limitações
técnicas de um banco de dados móvel não existem por incompetência técnica,
mas por limitação de hardware e por uma questão de design correto de
aplicações móveis. No âmbito de apps corporativos considere sempre o seu
device como uma extensão ao seu computador de trabalho, e não um
substituto definitivo ao mesmo.
SQLite
SQLite é uma biblioteca em linguagem C que implementa um banco de
dados SQL embutido. Ela foi criada e é mantida pela Apache Foundation e
programas que usem a biblioteca SQLite podem ter acesso a banco de dados
SQL sem executar um processo SGBD separado, mas também sem a robustez
dos bancos tradicionais, servindo como um excelente repositório móvel de
dados.
SQLite não é uma biblioteca cliente usada para conectar com um grande
servidor de banco de dados, mas sim uma biblioteca do próprio servidor, se é
que podemos chamá-lo assim. A biblioteca SQLite lê e escreve diretamente
para e a partir do arquivo do banco de dados no disco, com a extensão .db.
O uso do SQLite é recomendado onde a simplicidade da administração,
implementação e manutenção são mais importantes que incontáveis recursos
que SGBDs mais voltados para aplicações complexas possivelmente
implementam. As situações onde a simplicidade é a melhor escolha são muito
mais freqüentes do que pode-se imaginar.
Exemplos de uso do SQLite são:
● Sites com menos de cem mil requisições por dia
● Dispositivos e sistemas embarcados
● Aplicações desktop
● Ferramentas estatísticas e de análise
● Aprendizado de banco de dados
● Implementação de novas extensões de SQL
Não se recomenda o uso do SQLite para sites com:
● Muitos acessos
● Grande quantidades de dados (talvez maior que algumas dúzias de
gigabytes)
● Sistemas com grande concorrência
● Aplicações cliente/servidor
O SQLite:
● É Software Livre/domínio público e multiplataforma
● É um mecanismo de armazenamento seguro com transações ACID
● Não necessita de instalação, configuração ou administração
● Implementa a maioria da especificação SQL92
● Permite guardar o banco de dados em um único arquivo
● Suporta bases de dados abaixo de 2 terabytes
● Não tem dependências externas
Vale salientar também que muitos recurso tradicionalmente existentes em
SGBDs são inexistentes ou extremamente limitados no SQLite e entre eles
podemos citar:
● Afinidade por Tipos (Tipagem fraca): SQLite possui tipagem fraca, o
que permite retornar e escrever dados em formatos diferentes do que
foi proposto na especificação da tabela. Ele também trabalha com
pouquíssimos tipos como text (texto em geral), integer (inteiros), real
(ponto flutuante) e blob (binário).
● Alocação dinâmica: não precisamos definir o tamanho de cada coluna
da tabela, o SQLite varia o tamanho conforme a necessidade.
● Integridade Referencial: chaves estrangeiras até podem ser criadas,
mas não são respeitadas da maneira como esperamos.
● Transactions, Triggers e JOINs: extremamente limitados, não
recomenda-se o seu uso
● Stored Procedures e Functions: inexistente
Para saber mais sobre o SQLite e suas limitações, consulte o site oficial:
http://www.sqlite.org
Criando um Banco
Para se criar um banco de dados SQLite para o seu app (cada app tem o seu
próprio banco, que é armazenado no Internal Storage, dentro da pasta do
próprio app), você deve criar uma classe que extende SQLiteOpenHelper,
geralmente chamada de DbHelper. O DbHelper é responsável pela criação e
atualização do banco de dados, que nada mais é do que um conjunto de
tabelas (entenda que você não precisa dar um CREATE DATABASE ou algo
parecido) como mostrado na classe de exemplo a seguir, que criará um banco
com apenas uma tabela: Cliente, com ID autoincremental, nome, data de
nascimento, uma coluna indicando se ele é um cliente VIP, estado e cidade e
o sexo do cliente.
Note como temos de usar a criatividade uma vez que temos poucos tipos de
dados à disposição. Minha data de nascimento será text, no formato dd-mm-
yyyy, enquanto que a coluna VIP será um integer, com 0 ou 1 (sim ou não,
em representação binária). O sexo é text também, e conterá ‘m’ (masculino)
ou ‘f’ (feminino). Para finalizar, a sintaxe SQL dispensa uso de maiúsculas e
minúsculas em seus comandos, então descreva os comandos como preferir.
Código 101: disponível em luiztools.com.br/livro-android-fontes

public class DbHelper extends SQLiteOpenHelper {

static final String DATABASE_NAME = "CadCli.db";


static final int DATABASE_VERSION = 1;
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE Clientes(id integer primary key
autoincrement," +
"nome text not null, dataNascimento text not null," +
"vip integer not null, uf text not null, cidade text not null," +
"sexo text not null);");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion) {

}
}

As duas variáveis em maiúsculas logo no início da classe são constantes com


o nome do banco de dados desta aplicação (com extensão .db) e o segundo é
o número da versão do banco. Este número deve ser trocado quando quiser
atualizar o banco de dados em uma nova versão do app.
A classe ainda conta com um construtor que usa as constantes e dois
métodos: onCreate, chamado quando o banco for acessado pela primeira vez
e onUpgrade, chamado em caso de atualização do app com versão de banco
diferente.
O método onCreate
O onCreate deve conter os comandos SQL que serão executados durante a
criação do banco. Em SQLite não é necessário usar o comando CREATE
DATABASE, bastando criar as tabelas que você deseja, com todos os
campos. A sintaxe é a SQL ANSI e os tipos mais utilizados são text para
textos de qualquer tipo e tamanho, e integer para números inteiros. Note que
o comando SQL foi quebrado em múltiplas linhas pois era muito comprido,
mas que em seu projeto pode estar em uma linha só.
A variável db, do tipo SQLiteDatabase que é recebida por parâmetro nesse
método é o objeto que utilizamos para executar as instruções SQL sobre o
banco, com a chamada ao método db.execSQL passando a String SQL para o
mesmo. Recomenda-se a criação de cada tabela em uma chamada de
db.execSQL, para facilitar a leitura, manutenção e diminuir a chance de erros.
Para quem não lembra dos comandos DDL (Data Definition Language ou
Linguagem de Definição de Dados) do SQL, aqui vai o básico da estrutura:
Código 102: disponível em luiztools.com.br/livro-android-fontes

"CREATE TABLE NomeDaTabela(coluna1 tipo nullable, coluna 2 tipo


nullable);"

Dentro dos parênteses do comando CREATE TABLE devemos colocar o


nome da coluna seguido do seu tipo (integer, real, text ou blob) e
opcionalmente seguido de outras palavras reservadas (separadas por espaços
em branco) que costumamos chamar de constraints (restrições) tais como:
● Not null: quer dizer que esta coluna é obrigatório
● Autoincrement: esta coluna é auto-incremental, de 1 em 1
● Primary key: esta coluna é a chave primária da tabela, ou seja, não
pode se repetir e defina a ordenação básica da mesma
Outras constraints e comandos são possíveis e recomenda-se o estudo de
SQL, em específico os comandos suportados pelo SQLite para um melhor
entendimento e construção apropriada do banco local do seu app. Note
também que o onCreate sendo um método Java podemos explorar outros
recursos como leitura de arquivos (para pegar os comandos SQL de algum
lugar), conexão com dados externos (para atualizações, como veremos mais
adiante) e muito mais. Use a sua criatividade para criar o seu banco de acordo
com os requisitos da sua aplicação.
Atenção: o evento onCreate somente é disparado uma vez em seu app,
quando o arquivo.db ainda não existe e você tenta conectar no banco pela
primeira vez (como veremos a seguir). Note então que durante seus testes ele
somente será criado na primeira conexão, não importa quantos testes você
faça e isso pode acarretar comportamentos estranhos como o banco SQLite
não refletir alterações mais recentes na classe DbHelper durante o
desenvolvimento do app. Para resolver isso, toda vez que alterar sua classe
DbHelper (principalmente o método onCreate) exclua os dados da aplicação
(no gerenciador de aplicações, clicando no app que você criou e usando o
botão de “Limpar Dados”) ou desinstale o app (medida drástica). Outra
medida ainda mais drástica é excluir os dados do simulador (wipe data no
AVD Manager).

Usando o banco
Com a classe DbHelper, o próximo passo é criar a classe que irá executar
nossos comandos e consultas no banco de dados, que chamamos de
DbAdapter, conforme mostrado na imagem a seguir. Esta classe conterá os
métodos de abrir conexão com o banco de dados e envio de comandos e
consultas.
Na hora de abrir a conexão devemos escolher se queremos conectar no banco
em modo leitura ou em modo escrita, conforme utilizado no exemplo. Os
efeitos dos dois tipos de conexão são óbvios: você só pode dar comandos de
alteração de dados (INSERT, UPDATE e DELETE) se possuir uma conexão
com permissão de escrita (método getWritableDatabase), mas usar sempre
essa forma de conexão pode travar o seu banco de dados para uso por outros
processos da sua aplicação. Se você tiver certeza de que o banco não terá
qualquer tipo de acesso concorrente (que nem mesmo é encorajado com
SQLite) não terá muito o que se preocupar.
Outro método que vale salientar é o executarConsultaSQL que envia um
comando SELECT pro banco de dados e como retorna devolve um Cursor.
Um Cursor é um objeto que aponta para uma linha da tabela de retorno por
vez. Ou seja, se o retorno da consulta trouxer 10 linhas, o Cursor irá apontar
para uma de cada vez, devendo avançar após coletar os valores de cada
coluna de cada linha.
Mostrarei na sequência como manipular esse Cursor visando construir
objetos a partir dos dados.
Código 103: disponível em luiztools.com.br/livro-android-fontes

public class DbAdapter {

SQLiteDatabase db = null;
DbHelper dbHelper = null;

public DbAdapter(Context ctx){


dbHelper = new DbHelper(ctx);
}

private void abrirConexao(){


if(db == null)
db = dbHelper.getWritableDatabase();
}

public void fecharConexao(){


if(db!= null && db.isOpen()) {
db.close();
db = null;
}
}

public void executarComandoSQL(String sql){


abrirConexao();
db.execSQL(sql);
fecharConexao();
}

public Cursor executarConsultaSQL(String sql){


abrirConexao();
return db.rawQuery(sql, null);
}
}

Essa é uma classe genérica de uso de banco de dados SQLite. Nela pretendo
ter apenas métodos que podem ser utilizados independente de regras de
negócio ou de entidades específicas de um app ou outro, aumentando seu
reuso. Métodos específicos de entidades e do negócio devem ser
implementados em outras classes, como a GerenciadorClientes.java que
exemplifico abaixo, que inicialmente tem apenas um método de
retornarClientes que usa a DbAdapter por trás como era de se esperar.
Código 104: disponível em luiztools.com.br/livro-android-fontes

public class GerenciadorClientes {

private Context ctx;

public GerenciadorClientes(Context ctx){ this.ctx = ctx; }

public List<Cliente> retornarClientes(){


List<Cliente> clientes = new ArrayList<>();
DbAdapter dba = new DbAdapter(ctx);
Cursor cursor = dba.executarConsultaSQL("SELECT
id,nome,datanascimento,sexo,cidade,uf,vip FROM Clientes");

while(cursor.moveToNext()){
int id = cursor.getInt(0);
String nome = cursor.getString(1);
String data = cursor.getString(2);
String sexo = cursor.getString(3);
String cidade = cursor.getString(4);
String uf = cursor.getString(5);
boolean vip = cursor.getInt(6) == 1 ? true : false;
Cliente cliente = new Cliente(id,nome,sexo,data,vip,cidade,uf);
clientes.add(cliente);
}
dba.fecharConexao();

return clientes;
}
}

Neste método eu crio um novo adapter e mando executar uma consulta por
todos os clientes do banco de dados, com todas as suas informações,
recebendo como retorno um cursor que aponta para a tabela de retorno, que
pode ou não conter dados. Caso a tabela esteja vazia este código nem mesmo
entrará no while uma vez que o método moveToNext tenta avançar para a
primeira linha do resultado e se não conseguir retornará false.
Entrando dentro do laço usamos os métodos getInt e getString para pegar os
valores já convertidos de cada coluna e salvando em variáveis locais que
serão utilizadas para criar um objeto Cliente e adicioná-lo à nossa List que
será retornada após todos clientes serem instanciados e a conexão for
encerrada.
Note que o valor inteiro passado aos métodos getInt e getString é a posição
da coluna nas linhas retornadas pela consulta, motivo pelo qual fizemos o
SELECT informando as colunas em uma ordem específica.
Este procedimento está longe de ser o mais simples para uso com banco de
dados mas também não é muito complicado. É trabalhoso eu diria, mas como
é algo recorrente e não muda muito se quisermos ter tabelas Pedidos, por
exemplo, é algo que logo você dominará.
Abaixo listo a classe Cliente que estou utilizando, que nada mais é do que um
“espelho” da tabela do banco de dados, o que chamamos de ‘entidade’ do
banco de dados.
Código 105: disponível em luiztools.com.br/livro-android-fontes
public class Cliente implements Serializable {

int id;
String nome;
String sexo;
String dataNascimento;
boolean vip;
String cidade;
String uf;

public Cliente(String nome, String sexo, String dataNascimento, boolean


vip, String cidade, String uf){
this(0,nome,sexo,dataNascimento,vip,cidade,uf);
}

public Cliente(int id, String nome, String sexo, String dataNascimento,


boolean vip, String cidade, String uf){
this.id = id;
this.nome = nome;
this.sexo = sexo;
this.dataNascimento = dataNascimento;
this.vip = vip;
this.cidade = cidade;
this.uf = uf;
}

@Override
public String toString(){
return this.nome;
}
}

Mas e quando o comando SQL é personalizável? Ou seja, quando existe um


parâmetro que vai modificar o retorno, como fazemos? Vamos imaginar que
teremos um método muito parecido que receberá uma String por parâmetro,
representando uma busca por clientes com aquele trecho de nome. Olhe
abaixo como fica com dois métodos retornarClientes, um que permita uma
busca global (sem parâmetros e que retorna todos clientes sem distinção) e
outro que espera uma string contendo um nome para buscar, que usará para
filtrar os clientes a serem retornados. Como o resultado e o processo de
“montagem” da List de clientes é igual, fiz com que um método chamasse o
outro internamente.
Atenção: note o replace realizado sobre o parâmetro where para remover
aspas simples (apóstrofo) que possa estar contido na referida String. Isso é
necessário para evitar ataques de SQL Injection à base de dados através do
campo de busca. Saiba mais a respeito de como se prevenir de SQL injection
neste artigo da Wikipedia:
http://pt.wikipedia.org/wiki/Inje%C3%A7%C3%A3o_de_SQL
Código 106: disponível em luiztools.com.br/livro-android-fontes

public class GerenciadorClientes {

private Context ctx;

public GerenciadorClientes(Context ctx){ this.ctx = ctx; }

public List<Cliente> retornarClientes(){ return retornarClientes(null); }

public List<Cliente> retornarClientes(String nomeB){


String sql = "SELECT id,nome,datanascimento,sexo,cidade,uf,vip
FROM Clientes";
if(nomeB != null && !nomeB.equals(""))
sql += " WHERE nome LIKE '%" + nomeB.replace("'", "'") +
"%'";

List<Cliente> clientes = new ArrayList<>();


DbAdapter dba = new DbAdapter(ctx);
Cursor cursor = dba.executarConsultaSQL(sql);

while(cursor.moveToNext()){
int id = cursor.getInt(0);
String nome = cursor.getString(1);
String data = cursor.getString(2);
String sexo = cursor.getString(3);
String cidade = cursor.getString(4);
String uf = cursor.getString(5);
boolean vip = cursor.getInt(6) == 1 ? true : false;
Cliente cliente = new Cliente(id,nome,sexo,data,vip,cidade,uf);
clientes.add(cliente);
}
dba.fecharConexao();

return clientes;
}
}

Outro exemplo de método que espera parâmetros para montar o SQL correto
a ser executado (que basicamente é o que muda entre os métodos) seria um
método para salvar um cliente no banco de dados, que poderia ser como esse,
que apenas recebe os parâmetros e monta o INSERT que é executado na
tabela correspondente.
Código 107: disponível em luiztools.com.br/livro-android-fontes

public void salvarCliente(Cliente cliente){


DbAdapter dba = new DbAdapter(ctx);
String sql = String.format("INSERT INTO
Clientes(nome,sexo,cidade,uf,datanascimento,vip) VALUES
('%s','%s','%s','%s,'%s',%d)", cliente.nome.replace("'","''"),
cliente.sexo, cliente.dataNascimento, cliente.uf, cliente.cidade, cliente.vip ?
1 : 0);
dba.executarComandoSQL(sql);
}

Note como eu armazenei as informações do cliente recebido por parâmetro


em variáveis locais apenas para fins didáticos, para maior clareza e também
para remover os possíveis caracteres maliciosos que possam haver para tentar
invadir meu banco de dados através de inputs na futura tela de cadastro. Eu
construo o comando SQL INSERT colocando as variáveis nos locais certos
usando o método String.format, que basicamente permite que eu use
placeholders para Strings (%s) e ints (%d) ao longo do meu texto e passe
depois, separadas, as variáveis que quero usar, na mesma ordem dos coringas.
Essa prática é bem comum para construção de Strings complexas pois
privilegia a leitura e evita muitos erros de abre-fecha aspas, comuns em
Strings grandes e no caso específico de comandos SQL, fácil de gerar
confusão por causa das aspas simples (i.e. apóstrofe) e vírgulas ao longo do
comando (em SQL valores texto devem estar entre ‘ ’ – aspas simples.
Mas e se for uma atualização de um cliente existente? Como iremos
diferenciar isso? Bastam algumas modificações no mesmo método, como
veremos abaixo.
Código 108: disponível em luiztools.com.br/livro-android-fontes

public void salvarCliente(Cliente cliente){


DbAdapter dba = new DbAdapter(ctx);
String sql = cliente.id == 0
? "INSERT INTO
Clientes(nome,sexo,cidade,uf,datanascimento,vip) VALUES
('%s','%s','%s','%s','%s',%d)"
: "UPDATE Clientes SET
nome='%s',sexo='%s',cidade='%s',uf='%s',datanascimento='%s',vip=%d
WHERE id=" + cliente.id;

sql = String.format(sql, cliente.nome.replace("'","''"),


cliente.sexo,
cliente.cidade.replace("'","''"),
cliente.uf,
cliente.dataNascimento,
cliente.vip ? 1 : 0);
dba.executarComandoSQL(sql);
}

Note que o que realmente muda desta versão do método salvarCliente para a
anterior é o SQL construído, que caso o cliente passado por parâmetro possua
um ID, será um UPDATE ao invés de um INSERT. Com isso temos métodos
de busca, de retorno e de salvamento/atualização de clientes, faltando apenas
um de exclusão para incluir as operações elementares de sistemas de cadastro
(que vamos criar na sequência), os chamados CRUDs (Create, Retrieve,
Update e Delete, sigla comum entre os programadores para designar a
funcionalidade básica de gerenciamento de uma entidade do banco de dados
pelo sistema).
Código 109: disponível em luiztools.com.br/livro-android-fontes

public void excluirCliente(int id){


DbAdapter dba = new DbAdapter(ctx);
String sql = "DELETE FROM Clientes WHERE id=" + id;
dba.executarComandoSQL(sql);
}

Simples, não?! Claro, neste exemplo não estou considerando nenhuma regra
de negócio como verificar se este cliente PODE ser excluído, o que é bem
comum em sistemas comerciais.
E agora, usando as nossas classes DbHelper, DbAdapter, Cliente e
GerenciadorClientes, podemos criar um app de que tal criarmos um app de
‘Cadastro de Clientes’ para exercitar?
Exemplo: Cadastro de Clientes
Comece o novo projeto abrindo o Android Studio e indo em “Start a new
Android Project”, ou, se já estiver com o Android Studio aberto, apenas indo
no menu File > New Project.
Dê o nome de Cadastro de Clientes e avance com Next.
Escolha a opção “Phones e Tablets”, “Android 4.0.3” ou semelhante (o que
deve atingir mais de 97% dos smartphones do mundo) e avance novamente
com Next.
Escolha o template de Empty Activity e avance novamente com Next.

Deixe o nome como sugerido mesmo (MainActivity) e finalize o wizard de


construção do projeto com Finish.
Como todo bom app de cadastro devemos iniciar listando os clientes
existentes em nossa base em um layout com um ListView, apagando o “Hello
World” que já veio sozinho. Como segue:

O XML dessa tela deve ter ficado assim, com o componente ListView
possuindo o id ‘listView’ mesmo:
Código 110: disponível em luiztools.com.br/livro-android-fontes
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context="com.example.luizfduartejr.cadastrodeclientes.MainActivity">

<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView" />
</RelativeLayout>

Agora vamos programar para que este ListView seja carregado com todos os
clientes existentes em nosso banco de dados local. Para isso preciso que você
tenha implementado as classes que mostrei anteriormente neste tópico:
DbHelper, DbAdapter, Cliente e GerenciadorClientes, ficando com uma
estrutura de projeto como segue:
Assim, na classe MainActivity.java, mais precisamente no método onCreate
da Activity, vamos chamar nosso GerenciadorClientes para retornar todos os
clientes existentes no banco de dados e listá-los em nosso componente cujo id
é listView, como segue:
Código 111: disponível em luiztools.com.br/livro-android-fontes

public class MainActivity extends AppCompatActivity {

ListView listView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.listView);
carregarListagem();
}
public void carregarListagem(){
List<Cliente> clientes = new
GerenciadorClientes(this).retornarClientes();
if(clientes.size() == 0){
Toast.makeText(this, "Nenhum cliente cadastrado!",
Toast.LENGTH_SHORT).show();
}
ArrayAdapter<Cliente> clientesAdapter =
new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
clientes);
listView.setAdapter(clientesAdapter);
}
}

Note que está implícito a necessidade de alguns imports para este código Java
funcionar. Basicamente o Android Studio irá te sugerindo estes imports
conforme você for digitando o nome das classes, basta seguir as dicas
pressionando ALT+SPACE e em seguida ENTER para confirmar o import
que ele sugeriu.
Note também que estou usando um ArrayAdapter de Cliente ao invés de
Strings. Isso fará com que quando o Android for renderizar a ListView na
tela do smartphone, ele irá usar o método toString() da classe Cliente para
imprimir algo para o usuário. Para que ele imprima o que desejamos
realmente (por padrão apareceria o nome da classe), temos de sobrescrever o
método toString() da classe Cliente, como abaixo (se você está usando a
mesma classe Cliente citada anteriormente, este trecho de código já está lá):
Código 112: disponível em luiztools.com.br/livro-android-fontes

@Override
public String toString(){
return this.nome;
}

Obviamente se a gente executar esse código agora, quando ainda nem mesmo
temos a tela de cadastro do cliente, ele irá exibir o Toast de “Nenhum cliente
cadastrado!”. Então vamos deixar este código quieto por enquanto e vamos
adicionar uma nova nova activity em nosso projeto!
Com o botão direito do mouse sobre o package onde estão todas as classes do
seu projeto, escolha New > Activity > Empty Activity e dê o nome de
CadastroActivity à essa aqui, em seguida Finish.

Isso irá criar tanto a classe Activity quanto o layout da mesma, pois deixamos
o checkbox “Generate Layout File” marcado.
Agora vá em res > layouts e abra o novo layout XML que acabou de
aparecer, com o nome de activity_cadastro.xml. Vamos posicionar nele os
componentes necessários para cadastrar o cliente do nosso sistema, que
possui os atributos id (que será gerado automaticamente pelo banco), nome,
data de nascimento, VIP (um booleano true/false), uf, cidade e sexo.
No exemplo acima usei um EditText com hint para os campos de nome, data
de nascimento e cidade, definindo para cada um o inputType correto.
Ressalto também a “jogada” com um LinearLayout envolvendo o EditText de
cidade e o Spinner de estados, que eu eu populei com um XML de Strings
estáticas. Segue o código inicial:
Código 113: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

tools:context="com.example.luizfduartejr.cadastrodeclientes.CadastroActivity"

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:hint="Nome do Cliente"
android:ems="10"
android:id="@+id/txtNome" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="date"
android:hint="Data de Nascimento"
android:ems="10"
android:id="@+id/txtDataNascimento"
android:layout_below="@+id/txtNome" />

E o código específico do LinearLayout da cidade/estado, que vai logo abaixo


do código anterior. Definindo também um layout_weight de 7 para o campo
cidade e 3 para UF.
Código 114: disponível em luiztools.com.br/livro-android-fontes

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/localizacao"
android:layout_below="@id/txtDataNascimento"
android:orientation="horizontal">

<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="7"
android:inputType="textPersonName"
android:hint="Cidade"
android:ems="10"
android:id="@+id/txtCidade" />

<Spinner
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="3"
android:id="@+id/spnUf"
android:entries="@array/estados"></Spinner>

</LinearLayout>

Se você não lembra como adicionar um array de elementos estáticos a um


Spinner, apenas vá em res > values e adicione um “New > Values resource
file” lá com o conteúdo abaixo e o nome de estados.xml:
Código 115: disponível em luiztools.com.br/livro-android-fontes

<?xml version="1.0" encoding="utf-8"?>


<resources>
<string-array name="estados">
<item>RS</item>
<item>SC</item>
<item>PR</item>
</string-array>
</resources>

Dando sequência à construção do layout, o campo de sexo que é composto de


um TextView para o rótulo e um RadioGroup com dois RadioButtons dentro,
um para Masculino e outro para Feminino.
Código 116: disponível em luiztools.com.br/livro-android-fontes

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sexo:"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/lblSexo"
android:layout_below="@id/localizacao"/>

<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/grpSexo"
android:layout_toRightOf="@+id/lblSexo"
android:layout_below="@id/localizacao">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Masculino"
android:id="@+id/radMasculino"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Feminino"
android:id="@+id/radFeminino"/>
</RadioGroup>

E por fim, o campo VIP, que é um checkbox simples, seguido de um novo


LinearLayout para englobar dois botões de layout_weight 5 cada (para
ficarem com o mesmo tamanho sempre). Isso fecha o layout mostrado
anteriormente e nos deixa aptos a começar a programar os comportamentos
desta tela.
Código 117: disponível em luiztools.com.br/livro-android-fontes

<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Cliente VIP"
android:id="@+id/chkVip"
android:layout_below="@id/grpSexo"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/botoes"
android:layout_below="@id/chkVip"
android:orientation="horizontal">

<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="Cancelar"
android:onClick="btnCancelarOnClick"
android:id="@+id/btnCancelar"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:text="Salvar"
android:onClick="btnSalvarOnClick"
android:id="@+id/btnSalvar"/>

</LinearLayout>
</RelativeLayout>

Vamos começar a programar a classe definindo variáveis locais para fazer o


binding com os componentes de interface, o que será feito no onCreate.
Código 118: disponível em luiztools.com.br/livro-android-fontes

public class CadastroActivity extends AppCompatActivity {

Cliente cliente;
EditText txtNome, txtDataNascimento, txtCidade;
Spinner spnUf;
RadioButton radMasculino;
RadioGroup grpSexo;
CheckBox chkVip;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cadastro);
}
}

Em seguida, vamos continuar pelos métodos btnCancelarOnClick e


btnSalvarOnClick que foram nomeados nas respectivas propriedades onClick
dos botões Cancelar e Salvar da interface, como seguirão. Estes métodos
devem ser colocados dentro da classe CadastroActivity.java, logo abaixo do
método onCreate.
Para o botão Cancelar, o comportamento é bem simples: deve eliminar a
Activity atual para poder voltar à tela de listagem (MainActivity.java).
Código 119: disponível em luiztools.com.br/livro-android-fontes

public void btnCancelarOnClick(View v){


finish();
}

Para o botão de Salvar, a única complicação adicional (já que ele também
deverá voltar à tela de listagem após o salvamento, é de ler as variáveis que
representam os componentes da tela e depois enviar estas informações ao
gerenciador de clientes, como segue.
Código 120: disponível em luiztools.com.br/livro-android-fontes

public void btnSalvarOnClick(View v){


int id = cliente == null ? 0 : cliente.id;
String nome = txtNome.getText().toString();
String cidade = txtCidade.getText().toString();
String uf = spnUf.getSelectedItem().toString();
String dataNascimento = txtDataNascimento.getText().toString();
String sexo = radMasculino.isChecked() ? "M" : "F";
boolean vip = chkVip.isChecked();
Cliente c = new Cliente(id,nome,dataNascimento,sexo,vip,uf,cidade);
new GerenciadorClientes(this).salvarCliente(c);
Toast.makeText(this, "Cliente salvo com sucesso!",
Toast.LENGTH_LONG).show();
finish();
}

Atenção: alguns campos foram definidos como obrigatórios no banco de


dados. Para garantir a obrigatoriedade também no preenchimento dos campos
na interface, é aconselhável fazer validações se o nome foi digitado por
exemplo. Para isso, pode ser criado na classe GerenciadorCliente um método
validarCliente, por exemplo.
Código 121: disponível em luiztools.com.br/livro-android-fontes

public static boolean validarCliente(Cliente c){


if(c == null) return false;
if(c.nome == null || c.nome.equals("")) return false;
if(c.dataNascimento == null || c.dataNascimento.equals("")) return
false;
if(c.uf == null || c.uf.equals("")) return false;
if(c.cidade == null || c.cidade.equals("")) return false;
if(c.sexo == null || c.sexo.equals("")) return false;
return true;
}

Este método poderia ser chamado antes do GerenciadorClientes.salvarCliente


para impedir o salvamento caso algum campo não tenha sido preenchido.
O click do botão de Salvar termina com uma chamada de finish(), que
encerrará a Activity de cadastro e retornará para a Activity de listagem
(MainActivity). Do jeito que programamos anteriormente, a listagem somente
é atualizada quando abrimos o app pela primeira vez (onCreate), então vamos
adicionar um novo método na classe MainActivity.java, o onRestart, que será
disparado automaticamente pelo Android toda vez que esta tela voltar a ter o
foco do usuário, ou seja, quando ele acabar de cadastrar um novo cliente e
voltar para a listagem, ela será atualizada com o mesmo.
Código 122: disponível em luiztools.com.br/livro-android-fontes
@Override
protected void onRestart(){
super.onRestart();
carregarListagem();
}

Com isso deixamos tudo preparado para a volta após um cadastro completo!
No entanto, ainda não dissemos como que o usuário vai ir para a tela de
cadastro. Para que isso seja possível, devemos programar o menu da nossa
tela de listagem, para que ali exista a opção de cadastrar um novo cliente.
O código a seguir deve ser colocado dentro da MainActivity, logo abaixo do
método onRestart que criamos anteriormente e basicamente fornece uma
opção de menu “Novo Cliente”, que quando selecionado apenas abre a tela de
cadastro.
Código 123: disponível em luiztools.com.br/livro-android-fontes

@Override
public boolean onCreateOptionsMenu(Menu menu){
menu.add("Novo Cliente");
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item){
Intent intent = new Intent(MainActivity.this, CadastroActivity.class);
startActivity(intent);
return true;
}

Agora podemos testar o funcionamento básico de listagem e cadastro sem


problemas!
Mas como podemos fazer as funcionalidades de edição e exclusão de
clientes? Não é difícil!
A exclusão é bem simples: podemos programar para que quando o usuário
pressionar um toque longo sobre um cliente da lista, apareça a opção de
excluir aquele cliente específico, usando Dialogs, que nem vimos no capítulo
de interface e está exemplificado no código abaixo, do onCreate atualizado da
Activity de listagem.
Código 124: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.listView);
carregarListagem();

listView.setOnItemLongClickListener(new
ListView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {

final Context ctx = view.getContext();


final Cliente cliente = (Cliente)listView.getItemAtPosition(position);
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("Confirmação")
.setMessage("Tem certeza que deseja excluir este cliente?")
.setPositiveButton("Excluir", new
DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new
GerenciadorClientes(getBaseContext()).excluirCliente(cliente.id);
carregarListagem();
Toast.makeText(ctx, "Cliente excluído com sucesso!",
Toast.LENGTH_LONG).show();
}
})
.setNegativeButton("Cancelar", null)
.create()
.show();
return false;
}
});
}

Basicamente quando o usuário clica em Ok para a confirmação de exclusão, é


chamado o GerenciadorClientes.excluirCliente passando o ID do cliente que
acabou de ser clicado, para exclui-lo direto no banco de dados. Logo depois
manda-se carregar a listagem de novo para atualizá-la.

E a edição como fica? Bem simples também, basta que a gente trabalhe o
listener de click normal sobre um item da lista, ao invés do click longo como
fizemos anteriormente. Antes disso, temos apenas de fazer uma pequena
alteração na classe Cliente, para que seja possível transferir objetos cliente de
uma tela para outra: implementando a interface Serializable. Se a sua classe
Cliente foi criada de maneira idêntica ao do código anteriormente mostrado,
ela já está implementando Serializable.
Código 125: disponível em luiztools.com.br/livro-android-fontes

public class Cliente implements Serializable {

Então, podemos adicionar o listener de OnItemClick na listView, no


onCreate da MainActivity como segue:
Código 126: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.listView);
carregarListagem();

listView.setOnItemClickListener(new ListView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int
position, long id) {
Cliente cliente = (Cliente)listView.getItemAtPosition(position);
Intent intent = new Intent(MainActivity.this, CadastroActivity.class);
intent.putExtra("cliente", cliente);
startActivity(intent);
}
});
//o resto do código continua igual

Assim, quando clicarmos em um cliente da lista, o objeto do Cliente inteiro


será passado como Extra no Intent da nova Activity. Obviamente não
programamos nada na CadastroActivity para ler estas informações, que é
justamente o que vamos fazer agora.
Código 127: disponível em luiztools.com.br/livro-android-fontes

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cadastro);

txtNome = (EditText)findViewById(R.id.txtNome);
txtCidade = (EditText)findViewById(R.id.txtCidade);
txtDataNascimento = (EditText)findViewById(R.id.txtDataNascimento);
spnUf = (Spinner)findViewById(R.id.spnUf);
radMasculino = (RadioButton)findViewById(R.id.radMasculino);
chkVip = (CheckBox)findViewById(R.id.chkVip);
grpSexo = (RadioGroup)findViewById(R.id.grpSexo);

Intent intent = getIntent();


if(intent.hasExtra("cliente")){
cliente = (Cliente)intent.getSerializableExtra("cliente");
txtNome.setText(cliente.nome);
txtCidade.setText(cliente.cidade);
txtDataNascimento.setText(cliente.dataNascimento);

spnUf.setSelection((((ArrayAdapter)spnUf.getAdapter()).getPosition(cliente.

if(cliente.sexo.equals("M"))
grpSexo.check(R.id.radMasculino);
else
grpSexo.check(R.id.radFeminino);
chkVip.setChecked(cliente.vip);
}
}

No onCreate da classe CadastroActivity capturamos o Intent que chamou ela


e fazemos um teste para ver se veio a informação do cliente junto. Se veio,
extraímos as informações e populamos os campos da tela para que sejam
editados.

Note que salvo o objeto inteiro do cliente em uma variável da classe. Isso
porque vou usá-lo nos ajustes do botão Salvar, a seguir.
Código 128: disponível em luiztools.com.br/livro-android-fontes

public void btnSalvarOnClick(View v){


int id = cliente == null ? 0 : cliente.id;
String nome = txtNome.getText().toString();
String cidade = txtCidade.getText().toString();
String uf = spnUf.getSelectedItem().toString();
String dataNascimento = txtDataNascimento.getText().toString();
String sexo = radMasculino.isChecked() ? "M" : "F";
boolean vip = chkVip.isChecked();
Cliente c = new Cliente(id,nome,dataNascimento,sexo,vip,uf,cidade);
new GerenciadorClientes(this).salvarCliente(c);
Toast.makeText(this, "Cliente salvo com sucesso!",
Toast.LENGTH_LONG).show();
finish();
}

A mudança é sutil, agora pegamos a informação do id do cliente, se a tela foi


carregada a partir de um cliente já existente (veio de um click da listagem), e
é passada ao comando de salvar que, internamente, testa se veio um id junto
para executar um UPDATE no banco ao invés de INSERT. Ou seja, já deve
estar funcionando!
Poderíamos ainda colocar uma barra de pesquisa para usar o método de busca
da classe GerenciadorClientes, mas deixo essa tarefa para você concluir por
conta própria, são pequenos ajustes de interface!
E com isso encerramos um sistema completo de cadastro (CRUD) de clientes
usando banco de dados local!
7 ACESSO A DADOS REMOTOS
A maioria dos apps não é uma ilha isolada. Eles se conectam com servidores,
com outros apps e com APIs de terceiros ou da própria empresa que o criou.
Seja para obter atualizações, enviar dados ou mesmo executar a sua
funcionalidade principal é imprescindível o conhecimento de como acessar
dados remotos.
Programadores Java em geral estão acostumados a utilizar o framework
JDBC (Java DataBase Connectivity) para realizar conexões aos seus bancos
de dados favoritos como MySQL e SQL Server, diretamente. Entretanto, o
Android não oferece suporte nativo à conexões remotas em SGBDs e este
tipo de conexão deve ser realizado através de uma API (Application Program
Interface).
Essa restrição é motivo de muita confusão, alguns dos posts mais famosos do
meu blog são a respeito desse assunto, conexão remota de banco de dados
com Android. Acontece que, devido à inúmeros fatores, você não deve
conectar seu app diretamente a um banco de dados remoto. Ou seja, mesmo
que você consiga portar alguma biblioteca Java tradicional para o Android,
não é recomendado fazê-lo. Dentre os motivos estão a segurança, uma vez
que é muito fácil de fazer engenharia reversa em um app e descobrir sua
string de conexão com o banco. Outro motivo é qualidade da conexão com a
Internet, uma vez que as conexões com os bancos de dados foram projetadas
para serem operadas via LAN (rede local) e não via Internet, principalmente
Internet móvel, cuja qualidade é bem inferior à tradicional cabeada. Ou seja,
ou você precisará de um middleware de acesso à dados ou de uma API
hospedada em um servidor web.
Quando um software se conecta a outro dizemos que temos a arquitetura
cliente-servidor, onde quem oferece o serviço/informação é o servidor, e
quem consome é o cliente. Existem basicamente dois tipos de clientes que
nosso app pode ser: magro ou gordo e os aspectos técnicos de cada um pouco
diferem, sendo mais uma definição baseada nas restrições do seu cenário e no
uso da aplicação. Este assunto será melhor abordado mais à frente.
Usando APIs
Uma API é basicamente uma interface utilizada para conectar diferentes
sistemas, dispositivos ou a mescla entre ambos de maneira transparente e sem
criar uma dependência profunda entre ambos softwares, por assim dizer.
Quando conectamos em APIs famosas como Facebook e Twitter, não
precisamos conhecer os detalhes de como estes sites são criados e apenas
conhecer as chamadas e parâmetros das operações que queremos realizar é o
suficiente para construir os nossos aplicativos.
Criar apps que usam dados externos não é muito diferente do que criar com
dados locais, com exceção é claro das restrições de conectividade. O
desenvolvimento das classes, as abstrações com a orientação à objetos, as
telas do app, tudo permanece igual. Muitos sites de utilidade pública criam
APIs e as tornam de livre acesso (ou quase isso). Não é difícil encontrar na
Internet os mais variados serviços web (web services) que fornecem recursos
que podem ser integrados à sua aplicação corporativa. Alguns deles:
● Free Geo IP: fornece webservice onde através de um IP você pode
descobrir a localização aproximada do dispositivo. http://freegeoip.net
● Open Weather Map: fornece uma API de metereologia onde dado
uma latitude e longitude você consegue saber sobre a precipitação,
pressão atmosférica, velocidade do vento, etc naquele ponto.
http://openweathermap.org/
● Consulta de CEP: uma API gratuita de consulta de nome de endereço
a partir do CEP. http://avisobrasil.com.br/correio-control/api-de-
consulta-de-cep/
● Rastreamento de Encomendas: API para saber onde está a sua
encomenda dos Correios agora, com o código de rastreio.
http://developers.agenciaideias.com.br/correios/rastreamento
● Hora Local Mundial: API para saber a hora atual de qualquer cidade
do mundo. http://worldtime.io/api/
● MailTest: API para validar emails. http://www.mailtest.in
● INQStats: API aberta com dados demográficos mundiais.
http://blog.inqubu.com/inqstats-open-api-published-to-get-
demographic-data
● 99APIS: maior site brasileiro de APIs abertas .
http://www.99apis.com
Estes são apenas algumas APIs para ilustrar algumas possibilidades e, é claro,
você pode criar a sua própria API para consumir os recursos de outro sistema
seu, como ERPs e CRMs, ou apenas para realizar uma camada intermediária
para acesso ao seu SGBD externo. Mas antes de começar com planos mais
ousados, que tal usar uma das APIs citadas acima para ver como funciona
uma chamada à API externa?
O site http://freegeoip.net oferece uma API aberta e gratuita para descobrir a
geolocalização de um dispositivo com base em seu endereço IP. Ou seja,
você informa o IP e a API deles retorna um JSON, CSV ou XML com os
dados de localização, quando encontrados. Sim, o mesmo JSON que foi
citado anteriormente no capítulo sobre dados locais.
Você pode testar a API fazendo chamadas a
http://freegeoip.net/formato/seu_ip onde 'formato' deve ser substituído por
json, csv ou xml e 'seu_ip' deve ser substituído pelo IP que deseja consultar,
ou nada, para consultar a localização do seu IP atual. Neste exemplo
usaremos a URL http://freegeoip.net/json/8.8.8.8 que é o IP do servidor de
DNS do Google, na Califórnia, que retorna o seguinte resultado (uma String
JSON):
Código 129: disponível em luiztools.com.br/livro-android-fontes

{"ip":"8.8.8.8","country_code":"US","country_name":"United
States","region_code":"","region_name":"","city":"","zip_code":"","time_zone":"","latitud

Mas “o que isso tem a ver com acesso a bancos de dados remotos?”, você
deve estar se perguntando. O FreeGeoIP nada mais é do que uma base de
dados remota onde seus desenvolvedores criaram um webservice REST para
consulta de dados a partir de qualquer plataforma, ou seja, eles permitem
através da API que qualquer um possa acessar seu banco de dados, e é
exatamente isso que um desenvolvedor Android necessita fazer para integrar
de maneira prática e eficiente um banco de dados remoto ao seu app.
Exemplo: App com API
O primeiro passo é abrir nossa ferramenta, o Android Studio. Mande criar um
novo projeto do tipo Android Application e lhe dê o nome de LocationApp.
A versão de Android fica ao seu gosto, enquanto eu utilizei a versão 4.0 em
meus testes, oferecendo compatibilidade com toda a família IceCream
Sandwich e posteriores (JellyBean, KitKat, Marshmallow, etc).

Crie uma Blank Activity chamada LocationActivity que herdará da Activity


base e que criará ao mesmo tempo o arquivo de layout activity_location.xml
na pasta de layouts. Crie seu layout de maneira bem simples, usando widgets
padrões do Android SDK como TextViews, EditText e Button, como mostra
o trecho de código na página seguinte. O layout pretendido está acima, obtido
através do código XML a seguir.
Primeiro o topo, onde temos um LinearLayout para organizar
proporcionalmente o campo de texto e o botão independente da resolução:
Código 130: disponível em luiztools.com.br/livro-android-fontes

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LocationActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/ip">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="7"
android:text="177.155.44.231"
android:ems="10"
android:id="@+id/txtIP"
android:hint="Digite o IP"
android:inputType="number|text" />

<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="Carregar"
android:id="@+id/btnCarregar"
android:onClick="btnCarregarOnClick" />
</LinearLayout>

Na sequência, os TextViews que serão populados quando o botão de Carregar


for clicado e a API consultada:
Código 131: disponível em luiztools.com.br/livro-android-fontes

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Geolocalização"
android:textStyle="bold"
android:id="@+id/lblTitulo"
android:layout_below="@+id/ip" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="País: Brazil"
android:id="@+id/lblCountry"
android:layout_below="@+id/lblTitulo" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Estado: Rio Grande do Sul"
android:id="@+id/lblRegion"
android:layout_below="@+id/lblCountry" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Cidade: Gravataí"
android:id="@+id/lblCity"
android:layout_below="@+id/lblRegion" />
</RelativeLayout>

Com o layout criado, é hora de começar a programar nossa Activity, fazendo


o binding de cada widget com variáveis de mesmo tipo, como mostra o
trecho de código abaixo que fica no evento onCreate da LocationActivity.
Código 132: disponível em luiztools.com.br/livro-android-fontes

public class LocationActivity extends Activity {


EditText txtIP;
TextView lblCountry, lblRegion, lblCity;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location);

StrictMode.ThreadPolicy policy = new


StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

txtIP = (EditText)findViewById(R.id.txtIP);
lblCountry = (TextView)findViewById(R.id.lblCountry);
lblRegion = (TextView)findViewById(R.id.lblRegion);
lblCity = (TextView)findViewById(R.id.lblCity);
}
}

O próximo passo é criar uma classe para consumir a API do FreeGeoIP.net,


que chamamos de ClienteGeoIP, além de uma classe para representar o
objeto Localizacao do nosso app, que possui as referidas informações que
queremos exibir na tela. A classe ClienteGeoIP conecta-se na API usando um
cliente HTTP e convertendo o retorno para o formato JSON, usando as
bibliotecas nativas do Android.
Primeiro a classe Localizacao.java:
Código 133: disponível em luiztools.com.br/livro-android-fontes

public class Localizacao {


private String country;
private String region;
private String city;

public Localizacao(String country,


String region, String city){
this.country = country;
this.region = region;
this.city = city;
}

public String getCountry(){ return this.country; }


public String getRegion(){ return this.region; }
public String getCity(){ return this.city; }
}

Agora a classe ClienteGeoIP e seus métodos para consumir a API:


Código 134: disponível em luiztools.com.br/livro-android-fontes

public class ClienteGeoIP {

private static String readStream(InputStream in){


BufferedReader r = new BufferedReader(new InputStreamReader(in));
StringBuilder total = new StringBuilder();
String line;

try {
while ((line = r.readLine()) != null) {
total.append(line).append('\n');
}
}catch(Exception ex) {
ex.printStackTrace();
}
return total.toString();
}

public static String request(String stringUrl){


URL url = null;
HttpURLConnection urlConnection = null;
try {
url = new URL(stringUrl);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
return readStream(in);
}
catch (Exception e) {
e.printStackTrace();
}
finally {
urlConnection.disconnect();
}
return "";
}

public static Localizacao retornarLocalizacaoPorIp(String ip)


throws IOException, JSONException {
String content = request("http://freegeoip.net/json/" + ip);
JSONObject obj = new JSONObject(content);
String pais = obj.getString("country_name");
String estado = obj.getString("region_name");
String cidade = obj.getString("city");
return new Localizacao(pais, estado, cidade);
}
}

O método que mais importa aqui é o retornarLocalizacaoPorIP, que por sua


vez chama o método request que faz um HTTP GET na URL informada
(HTTP é o protocolo utilizado para transmitir e receber dados pela Internet).
O retorno da requisição é uma InputStream, que transformamos em String e
devolvemos-na como sendo o resultado, que nada mais é do que uma String
JSON contendo os dados do resultado da consulta (i.e. geolocalização por
IP).
Poderíamos tratar esta String de retorno de diversas maneiras sendo que a
melhor delas, sem dúvida, é quase sempre transformar em um objeto de
retorno que neste caso é um objeto JSON, representado pela classe
JSONObject da biblioteca org.json, nativa do Android SDK.
A classe JSONObject possui um construtor que recebe a String JSON e
através de chamadas get aos atributos do objeto podemos retornar seus
valores. Com os mesmos, podemos instanciar o nosso objeto localização.
Outro formato possível poderia ser a própria classe Localizacao ter um
construtor esperando um JSONObject e se montar a partir dele, o que tornaria
este trecho de código mais limpo.
Com o método de retornar localização criado, devemos definir o método do
evento OnClick do botão de carregar a localização da nossa LocationActivity
(note que existe uma chamada a um evento btnCarregarOnClick no XML do
Button, mostrado anteriormente). Esse método deve ser declarado na
LocationActivity, logo abaixo do evento onCreate:
Código 135: disponível em luiztools.com.br/livro-android-fontes

public void btnCarregarOnClick(View view){

//permite conexão com a Internet na Thread principal


StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

try {
String ip = txtIP.getText().toString();
Localizacao localizacao = ClienteGeoIP.retornarLocalizacaoPorIp(ip);
lblRegion.setText("Estado: " + localizacao.getRegion());
lblCity.setText("Cidade: " + localizacao.getCity());
lblCountry.setText("País: " + localizacao.getCountry());
}
catch(Exception ex){
Toast.makeText(getBaseContext(), ex.getMessage(), Toast.LENGTH_LONG).show();
}
}

O carregarLocalizacao nada mais faz do que chamar as classes criadas


anteriormentes, que, estas sim, fazem o “trabalho duro” de consumir a API, e
apenas exibe os resultados nos widgets definidos no seu layout. Não
esqueçamos também que uma vez que vamos conectar à Internet precisamos
informar essa permissão no AndroidManifest.xml, conforme mostrado
abaixo.
Código 136: disponível em luiztools.com.br/livro-android-fontes

<uses-permission android:name="android.permission.INTERNET" />

Testando nosso app em um AVD conseguimos ver que ele funciona


perfeitamente:
Criando APIs
APIs podem ser criadas de diversas maneiras e com os mais variados
propósitos. Praticamente toda linguagem de programação tem alguma forma
de criar uma API, especialmente as linguagens e frameworks web como PHP,
Ruby, JSP, Django e ASP.NET. A maioria dessas linguagens possui diversas
opções de formatação dos retornos de suas APIs como os citados
anteriormente: CSV, JSON e XML. Android não trabalha nativamente com
XML, o que requer bibliotecas de terceiros, e CSV não é muito utilizado na
web, então vamos ver como criar APIs que retornar JSON, que como
mostrado na seção anterior, bem simples de ser utilizado no app usando
poucas classes da biblioteca org.json.
Talvez não seja óbvio para programadores iniciantes, mas a(s) API(s) do seu
sistema devem ser desenvolvidas e hospedadas em algum servidor, ou seja,
representam um software à parte do seu app e geralmente demandam custos
adicionais à sua empresa ou contratante. Tenha isto em mente quando for
projetar a sua API e até mesmo escolher a tecnologia que será utilizado para
desenvolvê-la, tendo em vista as restrições de orçamento e infraestrutura.
API JSON COM PHP
Atenção: o foco deste livro não é o desenvolvimento PHP, então busque mais
conteúdo sobre os tópicos citados nesta seção em livros e sites, como o
excelente PHP.net. Além disso, você precisará de alguma outra IDE/editor de
código para programar PHP como PHP Storm, Sublime, Notepad++ ou
mesmo NetBeans e Eclipse. Também precisará de um servidor de aplicação
como Apache (com mod_php), IIS (como FastCGI) ou equivalentes.
PHP é basicamente um processador de scripts server-side para construção de
páginas, logo, basta que ao invés de construirmos páginas HTML comuns,
nosso script crie uma página com código JSON, para isso, poucos passos são
necessários, como mostrado pela imagem abaixo que representa o código de
uma página chamada index.php.
Código 137: disponível em luiztools.com.br/livro-android-fontes
<?php
// criamos um array com algumas informações básicas de uma pessoa
$person_info = array(
"nome" => "Luiz Fernando",
"idade" => 28,
"profissao" => "Professor",
"cidade" => utf8_encode("Gravataí")
);

//agora transformamos esse array em uma String JSON


$json = json_encode($person_info);

echo $json;
?>

Neste exemplo ao chamarmos essa página PHP no browser irá disparar a


execução deste script que constrói um objeto com dados de uma pessoa e
depois os transformam em um objeto JSON com o método nativo do PHP
json_encode. A notação JSON exige a formatação das strings em UTF-8,
então também foi realizada a conversão de todas as strings para esse formato,
a fim de evitar problemas. O resultado pode ser conferido ao acessarmos a
página index.php no browser, que provavelmente exibirá o conteúdo abaixo
sem tabulação ou quebras de linha.
Código 138: disponível em luiztools.com.br/livro-android-fontes

{
"nome": "Luiz Fernando",
"idade": 28,
"profissao": "Professor",
"Cidade": "Gravataí"
}

Você deve estar pensando: mas isto não é uma API, afinal sempre retorna os
dados da mesma pessoa! De fato, por isso que é imprescindível outros
conhecimentos para que nosso exemplo de impressão JSON em PHP se torne
uma API de verdade. Em primeiro lugar é imprescindível que se capture as
informações passadas por parâmetro na requisição HTTP GET que será
realizada pelos apps que forem criados. Em PHP, podemos capturar as
variáveis da querystring (parte da URL precedida por um ‘?’) usando a
variável pré-definida $_GET podemos passar o nome da variável da URL que
desejamos ler, para processar nosso script PHP de acordo. Imagine que temos
uma API de consulta de clientes com a seguinte URL:
Http://localhost:8080/index.php?nome=Luiz
Em nossa página busca.php precisamos ter o seguinte código para verificar o
nome do cliente que estamos buscando:
Código 139: disponível em luiztools.com.br/livro-android-fontes

$nome = $_GET["nome"];

Com base nesse nome podemos consultar em uma base MySQL (muito
utilizada em conjunto com PHP) por clientes semelhantes e retornarmos
todos no formato JSON, para serem listados depois em nossos apps.
Comandos comuns para realizar esta operação incluem os listados no site
oficial: http://php.net/manual/pt_BR/ref.mysql.php
Obviamente esta é uma das maneiras possíveis de se criar uma simples API
JSON com PHP. Existem diversos frameworks que facilitam enormemente
esta tarefa e que a tornam também mais padronizada de acordo com padrões
de mercado. Não estamos lidando aqui com questões de segurança, por
exemplo, ou com outros pormenores da linguagem, que cabe ao programador
averiguar visando uma API realmente funcional que possa ser implantada em
ambiente de produção.
API JSON COM ASP.NET
Nota: o foco deste livro não é ASP.NET, então serão dadas apenas
orientações básicas a respeito de como criar uma API com esta plataforma.
Busque os treinamentos oficiais da Microsoft para exemplos mais detalhados.
Existem atualmente duas formas largamente utilizadas para criação de APIs
JSON em ASP.NET: com WCF (Windows Communication Foundation) ou
com Web API (parte do ASP.NET MVC). Assim como no PHP, existem
outras maneiras de realizar esta tarefa, mas as citadas anteriormente são as
mais largamente utilizadas. Usaremos a primeira opção neste exemplo.
Para criar uma API JSON com ASP.NET iremos precisar da IDE Microsoft
Visual Studio, que inclusive já vem com o servidor web IIS Express. Nele
temos a opção criar novo projeto, onde escolheremos o template WCF
Service Application, utilizado para criação de webservices modernos com
tecnologia Microsoft.

Podemos definir o nome e localização do projeto no HD, mas o que importa é


que quando clicarmos em Ok teremos uma estrutura semelhante à essa
abaixo, onde temos alguns arquivos pré-definidos que eu apenas renomeei.
Neste exemplo é uma API de consulta de turmas para uma escola de idiomas.
O site da escola, que fica em um servidor diferente do sistema, precisa
consumir esta API para listar as turmas disponíveis aos possíveis alunos que
estejam visitando o mesmo. Note que uma API pode ser usada tanto por um
app mobile quanto por browsers e sistemas em qualquer dispositivo.
Voltando aos arquivos pré-definidos, o arquivo ITurmaService.cs é o que
chamamos de ‘contrato de serviço’ e nada mais é do que uma interface C# (a
linguagem padrão da plataforma ASP.NET) com os métodos que estarão
disponíveis na API, além de algumas anotações especiais, como segue:
Código 140: disponível em luiztools.com.br/livro-android-fontes

namespace Percutz.Api
{
[ServiceContract]
public interface ITurmaService
{
[OperationContract]
[WebGet(UriTemplate = "/EmAndamento", ResponseFormat =
WebMessageFormat.Json)]
List<TurmaDTO> RetornarTurmasEmAndamento();

[OperationContract]
[WebGet(UriTemplate = "/EmFormacao", ResponseFormat =
WebMessageFormat.Json)]
List<TurmaDTO> RetornarTurmasEmFormacao();

[OperationContract]
[WebGet(UriTemplate = "/Turma/{id}", ResponseFormat =
WebMessageFormat.Json)]
TurmaDTO RetornarTurma(string id);
}
}

Neste exemplo nossa API Possui três operações no seu contrato (assinaladas
com a anotação [OperationContract]), sendo a primeira para retornar as
turmas em andamento na escola, as turmas em formação ou retornar uma
turma através de um ID específico passado por parâmetro. A segunda
anotação logo abaixo de [OperationContract] diz respeito ao método HTTP
que deve ser utilizado para invocar este método (GET, nestes exemplos), o
path da URL (afinal, as chamadas aos métodos da API são com base na URL)
e o formato da resposta (JSON obviamente).
Como em qualquer interface teremos apenas a assinatura dos referidos
métodos que retornam objeto(s) do tipo TurmaDTO, o qual segue abaixo para
visualização (esta classe não aparece na imagem do projeto pois pertence à
outro projeto da mesma solução).
Código 141: disponível em luiztools.com.br/livro-android-fontes

namespace Percutz.Lib.DTO
{
[DataContract]
public class TurmaDTO
{
[DataMember]
public string Filial { get; set; }
[DataMember]
public string Nivel { get; set; }
[DataMember]
public short DiaSemana { get; set; }
[DataMember]
public short HoraInicio { get; set; }
[DataMember]
public short HoraFim { get; set; }

public TurmaDTO() { }

public TurmaDTO(Turma turma)


{
this.Filial = turma.Filial.Nome;
this.Nivel = turma.Produto.Nome;
this.DiaSemana = turma.DiaSemana;
this.HoraInicio = turma.HoraInicio;
this.HoraFim = turma.HoraFim;
}
}

A classe TurmaDTO também possuem algumas anotações que indicam que


essa é uma entidade de um contrato ([DataContract]) e que seus atributos
devem ser serializados no retorno de suas operações ([DataMember]). Por
fim, o construtor vazio é um requisito obrigatório de classes que serão
retornadas em webservices JSON, por isso sua existência, mesmo que sem
utilidade aparente.
Dando seguimento, agora temos de programar a implementação do serviço
em si, que é feita nos arquivos TurmaService.svc e TurmaService.svc.cs,
aninhados pelo Visual Studio. O arquivo TurmaService.svc somente pode ser
acessado se clicar com o botão direito sobe ele e escolhida a opção “View
Markup”, e garantindo que o conteúdo abaixo esteja sendo exibido. Caso
contrário (se der dois cliques no ícone) ele exibirá o conteúdo do
TurmaService.svc.cs, que não é o que queremos nesta etapa.
Código 142: disponível em luiztools.com.br/livro-android-fontes

<%@ ServiceHost
Factory="System.ServiceModel.Activation.WebServiceHostFactory"
Language="C#" Debug="true"
Service="Percutz.Api.TurmaService"
CodeBehind="TurmaService.svc.cs" %>

Após definir a marcação básica do serviço, devemos programar a


implementação C# do mesmo, baseado no contrato definido anteriormente.
Código 143: disponível em luiztools.com.br/livro-android-fontes

namespace Percutz.Api
{
public class TurmaService : ITurmaService
{
public List<TurmaDTO> RetornarTurmasEmAndamento()
{
return
ControladorTurma.RetornarTurmasEmAndamento(DateTime.Now.Year);
}

public List<TurmaDTO> RetornarTurmasEmFormacao()


{
return
ControladorTurma.RetornarTurmasEmFormacao(DateTime.Now.Year);
}

public TurmaDTO RetornarTurma(string id)


{
return ControladorTurma.RetornarTurma(int.Parse(id));
}
}
}
Note que estes métodos quase não possuem corpo, chamando outras classes
iniciadas com ‘Controlador’. Isso não te lembra o código do clique do botão
no app de consultar geolocalização por IP? O serviço web (API) não deve ter
a responsabilidade de ir no banco de dados diretamente ou de saber alguma
regra de negócio. A função dele é disponibilizar os dados, dados estes que
devem ser obtidos e processados por outra camada do seu software, neste
caso exemplificada pela classe ControladorTurma, esta sim que vai no banco
de dados (geralmente SQL Server) e obtém os dados a serem retornados.
Obviamente uma vez que em seu projeto não existe a lógica de acesso à
dados este exemplo não irá funcionar, mas a saída esperada para uma
chamada à API seria como abaixo (usando a URL
localhost/TurmaService.svc/EmFormacao, que deve retornar as turmas que
estão sendo formadas na escola):
Código 144: disponível em luiztools.com.br/livro-android-fontes

[{"DiaSemana":5,"Filial":"Cachoeirinha","HoraFim":1650,"HoraInicio":1415,"Nivel":"In
1"}]

Note que em nenhum momento precisamos manipular o JSON para criar o


retorno formatado. Isto é feito automaticamente pelo framework WCF, que é
uma biblioteca para serviços dentro do framework ASP.NET.
Por se tratar de um projeto de serviço web não precisamos nos preocupar com
o arquivo de configuração (web.config). De outro modo teríamos que
configurá-lo também.
API JSON COM JAVA SERVLETS
Nota: o foco deste livro não é o ensino de JSP ou Servlets, logo, os exemplos
citados serão breves e incompletos, cabendo ao desenvolvedor interessado
em utilizar esta tecnologia para construir suas APIs que busque conteúdos
mais completos, como as excelentes apostilas da Caelum.
Java Server Pages é o framework Java para desenvolvimento de aplicações
web. Páginas JSP são como páginas PHP, mas rodando scripts (chamados
aqui de scriptlets) escritos em Java. Também é permitido criar os chamados
Servlets, ou serviços web em JSP, muito utilizados para criação de APIs Java
para a web, execução de scripts Java sem interface gráfica, etc. Para este
exemplo usaremos o NetBeans, que já vem um servidor web embutido (para
este exemplo tanto faz o GlassFish ou o TomCat).
No NetBeans criaremos um novo projeto do tipo Java Web > Web
Application para desenvolvermos uma API de Calculadora em Java usando
Servlets.

O nome do projeto, que é informado na tela seguinte do assistente de novo


projeto, será CalculadoraWS.
Para este exemplo usarei o servidor GlassFish, que já vem com o NetBeans, a
versão 7 do Java EE e definirei o path do servlet como /CalculadoraWS

Não marquei nenhum framework na tela seguinte e como conclusão terei a


estrutura de projeto criada como ao lado, apenas com uma página HTML que
pode até mesmo ser excluída.
Para que possamos facilitar o desenvolvimento de uma API JSON com
Servlets recomendo o uso da biblioteca gratuita e open-source chamada
JSON Simple, que pode ser obtida em https://code.google.com/p/json-simple/
Baixe a última versão na seção de downloads e adicione o arquivo .JAR ao
seu projeto do NetBeans, clicando com o botão direito sobre a pasta Libraries
e selecionando a opção “Add JAR”. Com isso teremos acesso a algumas
classes para manipulação de JSON que veremos na sequência.
Voltando ao projeto, crie um Java Servlet na pasta “Source Packages”,
clicando com o botão direito na mesma e escolhendo New > Servlet e
escolhendo como nome CalculadoraServlet.

Na tela seguinte defina o nome do Servlet (geralmente terá o mesmo nome da


classe) o padrão de URL e marque o checkbox para que as informações sobre
esse Servlet já sejam adicionadas ao arquivo de configuração do projeto
(web.xml).
Isso irá gerar um arquivo CalculadoraServlet.java no seu projeto que é uma
classe Java tradicional, mas herdando de HttpServlet o que obriga esta classe
a sobrescrever alguns métodos como doGet, doPost e getServletInfo. Como
nossa API será muito simples vamos apenas implementar o conteúdo do
método doGet, que responde à chamadas HTTP GET vindas de outras
aplicações (como apps que possamos vir a criar).
Código 145: disponível em luiztools.com.br/livro-android-fontes

public class CalculadoraServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("application/json;charset=UTF-8");

double valor1 = Double.parseDouble(request.getParameter("v1"));


double valor2 = Double.parseDouble(request.getParameter("v2"));
double resultado = 0;
String operacao = request.getParameter("operacao");

switch(operacao){
case "Somar": resultado = valor1 + valor2; break;
case "Subtrair": resultado = valor1 - valor2; break;
case "Multiplicar": resultado = valor1 * valor2; break;
case "Dividir": resultado = valor1 / valor2; break;
}

try (PrintWriter out = response.getWriter()) {


JSONObject obj = new JSONObject();
obj.put("resultado", resultado);
out.println(obj.toJSONString());
}
}
}

Nosso método doGet inicia definindo que o tipo de conteúdo da resposta será
JSON com encoding UTF-8, o que garante a conformidade ao padrão. Depois
busca por três parâmetros que devem ser informados na querystring da
chamada à API: v1, v2 e operacao. Os dois primeiros são valores numéricos e
o terceiro é a operação matemática a ser realizada sobre eles, à saber: Somar,
Subtrair, Multiplicar ou Dividir. De acordo com a operação solicitada é
armazenado no resultado o processamento sobre os valores e sua consequente
conversão em JSONObject para impressão na saída da API. A classe
JSONObject usada para construção do objeto JSON de retorno é oriunda da
biblioteca JSON Simple que usamos anteriormente, então exige importação
da biblioteca com a diretiva import.
Os demais métodos do servlet podem ser deixados como padrão. Para testá-
lo, basta clicar com o botão direito sobre o servlet e escolher a opção “Run
File”, definindo na janela modal a URL de chamada ao servlet, incluindo a
queryString, como no exemplo abaixo.
O resultado é apresentado no seu browser, como segue, podendo ser alterada
a URL como quiser para efetuar outras operações.
Código 146: disponível em luiztools.com.br/livro-android-fontes

{ "resultado" : 5.0 }

Da mesma forma que com a API de geolocalização por IP, você pode chamar
este servlet em seu app para realizar operações matemáticas simples, ou então
ousar e criar APIs web extremamente complexas como APIs financeiras,
contábeis, jurídicas, etc. O céu é o limite quando falamos de webservices e
estas foram apenas três exemplos de tecnologias que podemos utilizar para
criar tais recursos para nossos apps corporativos em Android.
API JSON COM NODEJS
Nota: o foco deste livro não é NodeJS, então serão dadas apenas orientações
básicas a respeito de como criar uma API com esta plataforma.
Primeiro, caso ainda não possua o NodeJS instalado em sua máquina, baixe e
instale do site oficial: http://nodejs.org
Segundo, crie uma pasta para seus projetos node. Abra o prompt de comando
e acesse o diretório dos seus projetos Nodes, criado logo antes. Rode o
comando abaixo para criar um projeto Express dentro dela, com o nome de
apitest:
Código 147: disponível em luiztools.com.br/livro-android-fontes
C:\node>express apitest

Isso irá criar toda a estrutura básica de uma aplicação Express (um template
pronto para NodeJS), incluindo rotas default, o arquivo app.js com o coração
da aplicação e por aí vai.
Terceiro, abra o arquivo packages.json, que fica na raiz do seu projeto, para
configurarmos nossas dependências. Troque as dependências padrões que lá
estão pelas seguintes:
Código 148: disponível em luiztools.com.br/livro-android-fontes

"dependencies": {
"body-parser": ">=1.15.2",
"cookie-parser": ">=1.4.3",
"date-format": ">=0.0.2",
"debug": ">=2.2.0",
"express": "^4.14.0",
"mongodb": "^2.2.7",
"mongoose": "^4.8.2",
"morgan": ">=1.7.0",
"serve-favicon": ">=2.3.0"
}

Agora que modificou as dependências, rode o comando abaixo para instalar


todas dependências no seu projeto:
Código 149: disponível em luiztools.com.br/livro-android-fontes

C:\node\apitest>npm install

Quarto passo, sua aplicação Express deve estar funcionando agora, rode o
comando abaixo para executá-la e se certificar que fez tudo correto até aqui.
Código 150: disponível em luiztools.com.br/livro-android-fontes

C:\node\apitest>npm start
Acesse localhost:3000 no navegador e verá se a index aparece corretamente
(é apenas uma tela comum de olá mundo).
Agora vamos configurar o banco. Caso já possua um MongoDB configurado
e funcionando, ignore essa parte.
Se ainda não tem o MongoDB na sua máquina, baixe-o no site oficial:
http://mongodb.org. Instale/extraia os arquivos para seu C:\MongoDB ou
qualquer caminho que preferir.
Segundo passo, primeiro crie uma pasta data dentro da pasta da sua api.
Depois abra o prompt de comando e navegue até a pasta do seu MongoDB,
dentro dela terá uma pasta server/versão/bin e dentro dela você executará a
seguinte linha de comando, que iniciará o servidor Mongo dentro da API:
Código 151: disponível em luiztools.com.br/livro-android-fontes

mongod --dbpath c:\node\apitest\data\

Se aparecer a mensagem de "waiting connections", está tudo ok.


Terceiro passo, vamos adicionar alguns registros em nosso banco para não
iniciar com ele zerado. Neste exemplo irei inserir alguns clientes no banco,
que sempre é um bom exemplo comercial. Para isso, abra outro prompt de
comando e navegue até a pasta do seu MongoDB, dentro dela terá uma pasta
server/versão/bin e dentro dela você executará a seguinte linha de comando,
que iniciará o cliente Mongo:
Código 152: disponível em luiztools.com.br/livro-android-fontes

mongo

Uma vez com o cliente aberto, digite o seguinte comando para se conectar no
nosso banco da API:
Código 153: disponível em luiztools.com.br/livro-android-fontes

use apitest

E na sequência vamos inserir um array de registros de clientes:


Código 154: disponível em luiztools.com.br/livro-android-fontes

custArray = [{ "name" : "customer1", "email" : "cust1@testdomain.com" }, { "name" : "customer2",


"email" : "cust2@testdomain.com" }]
db.customers.insert(custArray);

Se quiser testar pra ver se funcionou, basta dar um


db.customers.fint().pretty() e serão listados todos os clientes cadastrados no
banco.
Com isso temos todo o ambiente pronto e configurado, agora vamos
programar!
Vamos usar o Mongoose aqui, que é a solução de ORM mais profissional
para usar MongoDB com NodeJS. Mais informações em
http://mongoosejs.com
Primeiro passo, crie um arquivo db.js na raiz do seu projeto apitest. Dentro
dele, cole o seguinte código, que explicarei na sequência:
Código 155: disponível em luiztools.com.br/livro-android-fontes

var mongoose = require('mongoose');


mongoose.connect('mongodb://localhost/apitest');

var customerSchema = new mongoose.Schema({


name: String,
email: String
}, { collection: 'customers' });

module.exports = { Mongoose: mongoose, CustomerSchema:


customerSchema }

Aqui eu criei um objeto Mongoose, fiz a conexão com nosso banco que está
rodando local e defini o schema da coleção de clientes no banco de dados,
usando o mesmo nome que já tinha usado anteriormente, customers. Por fim,
exportei um objeto contendo o Mongoose e o schema, para uso posterior.
Segundo passo, vamos testar. Para fazer isso, vamos fazer o método mais
elementar e básico da API: o GET por todos os clientes da base. Mais pra
frente você pode implementar elementos importantes como paginação, mas
por ora, vamos retornar todos. Abra seu arquivo index.js dentro da pasta
routes do seu projeto. Coloque o seguinte código logo acima do
module.exports:
Código 156: disponível em luiztools.com.br/livro-android-fontes

/* GET all customers. */


router.get('/customers', function (req, res, next) {
var db = require('../db');
var Customer = db.Mongoose.model('customers', db.CustomerSchema,
'customers');
Customer.find({}).lean().exec(function(e,docs){
res.json(docs);
res.end();
});
});

Esse código basicamente pega todas requisições GET para a URL


localhost:3000/customers e retorna todos os clientes cadastrados no banco no
formato JSON, diretamente no corpo da resposta, que é o esperado para uma
API. Salve esse arquivo e reinicie o servidor NodeJS, para que as alterações
surtam efeito.
Terceiro passo, vamos pro navegador testar: acesse localhost:3000/customers
e você deve ver um array JSON com todos os customers salvos no banco,
quando inserimos a carga de exemplo.
Agora vamos alterar nosso código para retornar apenas um cliente, algo bem
comum em APIs, uma vez que nem sempre você vai querer retornar todos os
elementos de uma coleção do banco.
Para isso, basta criarmos uma nova rota /customers para também aceitar um
ID logo após seu path, algo como /customers/123-abc-456-def (lembrando
que no MongoDB usamos guids alfanuméricos). O código abaixo mostra
como seria essa nova rota:
Código 157: disponível em luiztools.com.br/livro-android-fontes

/* GET ONE customers. */


router.get('/customers/:id', function (req, res, next) {
var db = require('../db');
var Customer = db.Mongoose.model('customers', db.CustomerSchema,
'customers');
Customer.find({ _id: req.params.id }).lean().exec(function (e, docs) {
res.json(docs);
res.end();
});
});

Note o uso de :id indicando que o parâmetro logo após /customers será o ID
do mesmo. Note também o uso de um filtro em nosso find, baseado no _id do
documento.
Para conseguir testar, primeiro você terá de descobrir o _id de um cliente que
já esteja no banco. Para isso, use linha de comando "mongo", que fica dentro
da pasta bin do MongoDB, executando um db.customers.find().pretty() e
copiando o _id de qualquer um.
Salve o arquivo, reinicie seu servidor Node e o resultado de uma pesquisa
com ID é o objeto JSON com todos os dados daquele customer.
O próximo passo é criarmos uma rota que permita adicionar novos clientes
em nossa coleção. Segundo a especificação HTTP, o verbo POST é usado
quando queremos adicionar novos elementos, então é isso que faremos, uma
rota que manipule o POST em nossa API, como no exemplo abaixo.
Código 158: disponível em luiztools.com.br/livro-android-fontes

/* POST ONE customer. */


router.post('/customers/', function (req, res, next) {
var db = require('../db');
var Customer = db.Mongoose.model('customers', db.CustomerSchema,
'customers');
var newcustomer = new Customer({ name: req.body.name, email:
req.body.email });
newcustomer.save(function (err) {
if (err) {
res.status(500).json({ error: err.message });
res.end();
return;
}
res.json(newcustomer);
res.end();
});
});

Note que este código já começa diferente dos demais, com router.post ao
invés de router.get, indicando que está rota tratará POSTs no endpoint
/customers. Na sequência, carregamos o objeto db para pegar o model
customers do Mongoose, criarmos um novo customer com o name e o email
que vieram no body da requisição (incluindo aqui requisições com body em
JSON) e depois mandamos ele salvar no banco.
Nosso save também está um pouco diferente do exemplo do post de
introdução, pois aqui estamos tratando o caso de dar algum erro. Neste caso
enviamos um código HTTP 500 ao consumidor da API, com um JSON
contendo a mensagem de erro. De outra maneira, em caso de sucesso,
devolvemos o objeto customer que acabou de ser criado, incluindo o _id que
recebeu.

Salve seu arquivo e reinicie o servidor Node. Para testar, você terá de usar
alguma ferramenta que forje requisições HTTP como POSTMAN ou Fiddler.
Ou ainda realizar requisições POST a partir do seu app Android.
Agora que já podemos consultar todos clientes, consultar um cliente e salvar
um novo cliente, é hora de permitirmos que os usuários de nossa API (ou
seja, o seu app Android) atualizem os dados dos clientes.
A especificação para APIs HTTP REST define que nosso endpoint deve
esperar receber o id do objeto que queremos atualizar na URL, no formato
/customers/id, enquanto que no corpo da requisição devemos enviar os dados
que serão alterados no objeto em questão.
Sendo assim, vamos configurar mais uma rota em nosso arquivo
/routes/index.js:
Código 159: disponível em luiztools.com.br/livro-android-fontes

/* PUT ONE customer. */


router.put('/customers/:id', function (req, res, next) {
var db = require('../db');
var Customer = db.Mongoose.model('customers', db.CustomerSchema,
'customers');
Customer.findOneAndUpdate({ _id: req.params.id }, req.body, { upsert:
true }, function (err, doc) {
if (err) {
res.status(500).json({ error: err.message });
res.end();
return;
}
res.json(req.body);
res.end();
});
});

Neste exemplo estamos configurando uma rota PUT para /customers/id. O


processo inicial do algoritmo não é muito diferente dos demais, onde
carregamos o objeto db, o model Customer e com ele chamamos o método
findOneAndUpdate que, baseado na query {_id: req.params.id} irá alterar o
objeto com o req.body passado (que é o JSON que enviaremos na requisição
PUT com os valores atualizados do customer).
O próximo parâmetro, upsert:true, indica que se o customer não existir, ele
será criado. Fica a seu critério manter esta configuração assim ou não. Já a
nossa função de callback é a de sempre: retornará o erro em caso de falha ou
o objeto JSON que foi salvo se tudo der certo.
Salve e reinicie o seu servidor Node para poder testar. Apenas lembre-se que
para um PUT funcionar você deve enviar o ID do customer a ser atualizado
pela URL, e o objeto JSON com os novos dados no body da requisição.
E para finalizar o CRUD da nossa API, vamos criar uma rota que espere um
verbo DELETE com o id do customer a ser excluído na URL. Fácil, fácil!
Código 160: disponível em luiztools.com.br/livro-android-fontes

/* DELETE ONE customer. */


router.delete('/customers/:id', function (req, res, next) {
var db = require('../db');
var Customer = db.Mongoose.model('customers', db.CustomerSchema,
'customers');
Customer.find({ _id: req.params.id }).remove(function (err) {
if (err) {
res.status(500).json({ error: err.message });
res.end();
return;
}
res.json({success: true});
res.end();
});
});

Essa nossa nova e última rota trata requisições com o verbo HTTP DELETE
no endpoint /customers/id. Para excluir o documento correspondente da nossa
coleção basta darmos um find por _id usando o id que veio na URL e
chamando a função remove logo em seguida, que removerá o documento que
for retornado pelo find. O conteúdo do callback do remove dispensa
comentários.
Para testar o delete, basta chamar a URL /customer/{id}, trocando pelo ID do
customer a ser excluído.
8 CONSIDERAÇÕES FINAIS
O livro vai chegando no fim e gostaria de entrar em dois tópicos bem
importantes para quem está dando seus primeiros passos com apps Android:
projeto e publicação, ambos relacionados à ganhar dinheiro com os apps
desenvolvidos. Enquanto que o objetivo principal do livro foi atingido: ser
um guia para iniciantes no desenvolvimento de apps para empresas, quero
deixar algumas dicas relacionadas à análise e projeto de aplicativos, fruto de
minhas experiências fazendo apps para empresas de todos os tipos e também
um guia rápido de como fazer a publicação na Google Play, a maior loja de
aplicativos para Android, embora diversos projetos de apps para empresas
jamais sejam publicados na referida loja, sendo instalados diretamente nos
smartphones e tablets dos funcionários da empresa.
Projetos de Apps para empresas
Desenvolver apps para terceiros não é difícil. Não precisa ter uma ideia
mirabolante, você pode pegar só trabalhos que se adequem ao seu nível de
experiência e ainda por cima, a curto prazo paga muito melhor do que um app
tradicional (aqueles que ganham com publicidade ou cobram pouco por
download). Mas como funciona?
Basicamente alguém ou alguma empresa te contrata com alguma demanda.
Eles te dão um briefing do projeto, você faz um orçamento e se tudo der certo
o cliente aprova, às vezes exigindo a assinatura de um termo de
confidencialidade e cessão de direitos autorais do projeto. Você vai
desenvolver o projeto em casa mesmo e ao término, irá entregar o código-
fonte e o app pronto ao contratante para receber o pagamento.
Particularmente eu sempre ganhei dinheiro com apps assim, é a maneira mais
rápida e fácil de ganhar quantias interessantes, como alguns milhares de R$,
em pouco tempo de trabalho, de um final de semana a uma semana, em
média. Fiz apps para empresas como Embelleze, LG, Ford e Renault que
foram usados em eventos e campanhas publicitárias. Essa experiência com
apps para empresas inclusive foi o catalisador para ter escrito este livro.
Se soa estranho trabalhar de casa para grandes empresas, saiba que são
exatamente as grandes empresas que melhor se adequam a esse modelo de
trabalho. Reuniões são feitas via Skype e geralmente elas costumam ligar
diariamente ou dia-sim/dia-não para saber sobre o andamento do projeto, em
alguns casos mantendo uma planilha ou uma série de documentos
compartilhados entre os membros do projeto (você, quem lhe contratou,
quem está pagando pelo projeto, etc) para todos terem mais visibilidade. Não
tem mistério, e dá para trabalhar de pijamas! :D
Mas como ser "descoberto" pelas empresas como um programador de apps?
Arranjando freelas
A chave aqui é freelancing, também conhecido como "bico", aqui no Brasil.
Para que encontrem você como um desenvolvedor de apps, você deverá se
cadastrar na maioria dos sites de freelancing que encontrar, como
Freela.com.br, GetNinjas.com.br e Prolancer.com.br (brasileiros), e se tiver
Inglês avançado, no Workana e no Freelancer.com. Todos esses sites
permitem que você filtre quais tipos de trabalho deseja receber por email e
você não é obrigado a fazer nenhum trabalho que não se sinta confortável
(que não tenha os conhecimentos por exemplo).
Só isso pode não bastar, então coloque essa informação no seu perfil do
LinkedIn e de repente tire aquela imagem genérica de pôr-do-sol ou de
cachorro da capa do seu Facebook e coloque um banner oferecendo seus
serviços lá. Se tiver um blog (como o meu LuizTools.com.br) escreva a
respeito de desenvolvimento de apps. Você não tem ideia de quantos
trabalhos bons e rentáveis meu blog já me gerou.
Outra ideia é ser conhecido pelas agências digitais. Toda vez que uma
empresa grande quer um app, ela não abre os classificados do jornal para ver
se alguém quer desenvolver para ela. Ela contrata uma agência de publicidade
ou uma agência digital, que são empresas que tocam demandas ligadas à
marketing, desenvolvimento e redes sociais para outras empresas. Diversos
apps que eu fiz vieram de indicação de pessoas que eu conheço dentro dessas
agências, que geralmente terceirizam a parte do desenvolvimento para
pessoas externas, como eu. Assim, dê uma procurada na Internet por essas
agências (procure termos como agência de publicidade, agência digital e
produtora digital) e mande email para os responsáveis pelos projetos,
oferecendo os seus serviços.
Mas e se eles me perguntarem quanto irá custar o app, o que eu digo?
O quanto cobrar?
Cobrar por desenvolvimento de software é uma arte. Não vou mentir, já fiz
muito projeto quase de graça, mas também já fiz muito projeto no melhor
estilo "custo Brasil". Cobrar vai muito além de calcular as horas que você
acha que vai levar e multiplicar pelo seu valor hora. Tem a ver com tipo de
projeto, dificuldade, tamanho do cliente, etc. Só para dar um exemplo, se um
cliente grande como LG bate à sua porta querendo um app, e você calcular
que vai demorar 20h pra fazer e cobra R$600 porque sua hora custa R$30
atualmente, eles não vão fechar com você. Eles vão fechar com alguém que
cobre uns R$2.000 deles, no mínimo (R$100/h), porque querem um
profissional, não um amador. Repare que o preço está associado à percepção
de qualidade da entrega.
Agora, se o cliente é o padeiro da esquina, que quer ter um app para controlar
o estoque de farinha, ele não vai querer pagar R$2.000 pra isso, pois
geralmente softwares de comércio genéricos custam menos de R$1.000.
Estou usando exemplos simples e pouco precisos, apenas para você entender
melhor. Mas então, o quanto cobrar?
Se estiver usando as plataformas de freelancers que mencionei antes, o
próprio cliente estipulará o valor-limite do projeto, ou seja, se quiser ser
aceito (se achar que vale a pena e que sabe fazer o que ele pede), terá de dar
um "lance" dentro desse orçamento. Obviamente tem clientes muito sem
noção, que querem um WhatsApp pagando R$500, ou um Uber pagando
R$2.000 (este último realmente aconteceu).
Se não estiver usando as plataformas acima, ou mesmo com elas, terá de
experimentar, ver o cálculo que funciona para você. Como eu faço? Bem,
fazer apps não é minha ocupação principal, então encaro como hora extra
(+50% do valor hora que recebo no meu serviço). Se for fazer no final de
semana, a hora extra é dobrada (x2 o valor hora). Se for em uma época que
estou muito ocupado, costumo aumentar ainda mais, para fazer valer a pena o
stress de tocar muita coisa ao mesmo tempo, bem como se for um projeto
muito chato, onde por exemplo, não vou aprender nada de novo. Estipulado
isso, calculo quantas horas vou levar em cada tela e/ou funcionalidade do
app, para multiplicar pelo valor hora que descobri antes e chegar no preço
final. Coloque mais uns 50% para cobrir testes, correção de bugs, ajustes que
o cliente vai pedir e desafios que vai encontrar que não havia previsto e voilá,
temos um cálculo mais ou menos pronto que você pode usar.
Exemplo prático: Joãozinho trabalha de dia e faz apps de noite, como
freelancer. Ele ganha R$1600/mês em seu trabalho, ou R$10/h, e como faz
apps à noite (hora extra de 50%), ele vai cobrar R$15/h. Joãozinho pega um
projeto de app de TODO List (lista de tarefas), onde tem uma tela de listagem
de tarefas, que ele acha que faz em 4h, e uma tela de cadastro de tarefa,
outras 4h. Ele ainda vai ter de codificar algumas classes Java, principalmente
a que conecta com o banco, totalizando umas 12h de trabalho. Joãozinho vai
adicionar 50% mais horas para os testes e ajustes e cobrar ao todo 18h de
trabalho ou R$270 por esse app. Uma pechincha!
Outra dica para cobrar melhor, é usar a geoarbitragem ao seu favor, ou seja,
vender seus serviços apenas para quem pode pagar mais, que está em um
local mais favorecido que o seu. Sites internacionais de freelancer (como o
freelancer.com) permitem que você oferte seus serviços para países
específicos, como EUA por exemplo, recebendo em dólar. Se o Joãozinho
vendesse o mesmo app para um americano, ele poderia converter os R$270
em U$270 dólares (o Joãozinho não está nem aí pro câmbio atual) e faturar
R$891 com o mesmo app!
Ok, se você não tem Inglês avançado (sorte do Joãozinho, ele tem), dá para
levar vantagem com Portugal (que paga em Euro, que vale mais que o dólar)
ou aqui no Brasil mesmo, vendendo só pro eixo Rio-São Paulo, onde o se
paga mais por serviços de profissionais de TI. Sério, já fiz isso muitas vezes,
de recusar projeto gaúcho para ganhar o dobro com projeto paulista.
Se ainda assim você não quiser vender projetos de apps para terceiros, talvez
por não gostar de lidar com clientes, ainda há a esperança de ganhar dinheiro
na Google Play, conforme menciono a seguir.
Publicação na Google Play
Como já mencionei bastante ao longo deste livro, meu foco não é ensinar a
criar apps de diversão e entretenimento, por isso você pode pensar que seu
app não precisa estar disponível na Google Play. Isto é quase verdade.
Alguns apps para empresas não são exatamente dedicados a um único cliente.
Um app de calculadora de correção monetária para advogados é um app para
empresas, e não precisa ser um app exclusivo de apenas um escritório de
advocacia, certo?! Principalmente quando falamos de apps para profissionais
liberais, como advogados, médicos, nutricionistas, profissional trainers, etc,
estamos falando de apps focados em necessidades de
empresas/empreendedores e estes são apps que podem muito bem serem
rentabilizados na Google Play.
Eu não vou falar aqui de monetização de apps, um assunto que de vez em
quando volta à tona em meu blog, mas cabe uma ressalva: um app bem
construído para profissionais liberais pode, e deve, ser cobrado “caro”, em
comparação com outros apps. Como “caro’ entenda que fuja bem ao padrão
de $0.99 indo parar nas cada de $10, dependendo da utilidade para o
profissional. A HP fez milhões com seu app que simula a calculadora
HP12C, líder em vendas mundial, especialmente nos segmentos de
contabilidade e economia.
Mas voltando ao que interessa, nesta seção vou dar uma visão geral de como
publicar seu app na Google Play. Digo visão geral pois não é um processo
exatamente complicado, e também não é um processo muito definido, uma
vez que eles podem mudar algumas regras sem aviso prévio.
O primeiro passo é criar uma conta de desenvolvedor no Google. Acesse o
link abaixo e faça seu cadastro:
https://play.google.com/apps/publish/
Na data em que escrevo este livro o Google cobra U$25 de taxa única para se
cadastrar como desenvolvedor. Para realizar este pagamento você terá de ter
uma conta na Google Wallet, a carteira virtual do Google, a mesma que
talvez você já tenha conta caso já tenha comprado algum app (ou feito gastos
em apps com compras internas) na Google Play. Pague a taxa com seu cartão
internacional, confirme sua conta de e-mail e siga em frente.
Aviso: você notou que precisará de um cartão internacional, certo? Se você
for menor de idade terá de pedir isso aos seus pais. Se for maior de idade e
estiver estudando, a melhor opção é pegar um cartão internacional junto a
uma conta universitária em qualquer banco grande como Santander. Caso não
esteja estudando (ou não queira uma conta universitária), mas esteja
trabalhando de carteira assinada, você pode pedir um cartão internacional na
sua agência. Caso não tenha conta em banco algum, sugiro pedir um cartão
Nubank (http://www.nubank.com.br) ou um Dollar Card da Ebanx
(http://www.ebanx.com.br). O primeiro não tem anuidade mas exigirá análise
de crédito. O segundo é pré-pago e tem taxas.
Voltando à Google Play, após criar sua conta acesse novamente o Console do
Desenvolvedor a Google Play:
https://play.google.com/apps/publish/
Você deverá ver a tela abaixo.

Clique no botão azul no canto superior direito, onde está escrito “Adicionar
novo app”, que exibirá a tela a seguir. Escolha o idioma do seu app, o Título
dele e clique em “Enviar APK”.
Note que quando chegar nesta etapa você já deverá ter terminado seu app, os
testes do mesmo (incluindo em dispositivos físicos) e ter gerado o APK de
release para subir à loja.
Após clicar no botão “Enviar APK” você deverá ver a tela abaixo, para envio
do mesmo. Eu omiti o nome do meu APK por questões de privacidade
(retângulo amarelo).

Nesta tela você deve clicar em “Enviar o primeiro APK para produção” e
subir a versão release do mesmo.
Atenção: caso você suba a versão errada do APK (versão debug) o console
de publicação irá lhe xingar. Para gerar a versão release é bem simples: no
Android Studio, vá em Build > Generate Signed APK e crie suas chaves de
publicação com seus dados pessoais e uma senha à sua escolha. O assistente
de geração da chave vai pedir um local para armazenar a chave, escolha um
seguro obviamente. Depois que o apk for gerado, basta enviar ele pelo
console, como mencionado anteriormente.
Depois de enviar seu APK existem muitas burocracias a serem atendidas em
cada um dos menus à direita do console de publicação, principalmente na aba
Detalhes do App. Os principais pontos são listados abaixo:
● Título: já aparecerá o nome fornecido antes, e você pode alterá-lo
aqui.
● Breve descrição: resuma o seu app em uma ou duas frases, que caibam
em 80 caracteres.
● Descrição completa: aqui vai aquele texto marqueteiro para convencer
o visitante da Google Play a baixar seu app, com até 4000 caracteres.
● Recursos gráficos: nesta seção você deve fazer upload das telas do seu
app. O ideal é que você execute ele no simulador e tire print-screens
pelo computador, recortando apenas a área que interessa para
divulgação do app. Não esqueça de nenhuma tela importante e, é claro,
da “capa” do app. Cada imagem tem de ter no mínimo 320px de
largura para smartphone e você pode subir imagens diferentes para
tablets, TVs, e etc, para ajudar o usuário a entender como o app ficará
em seu dispositivo.
● Ícone: você deve subir apenas um ícone de alta resolução (512x512px)
que será usado e redimensionado à vontade da Google Play.
● Gráfico de recursos: aqui vai o banner do seu app, na resolução
1024x500px.
● Video promocional: existe a possibilidade de adicionar uma URL do
Youtube com o vídeo do seu app funcionando.
● Categorização: aqui você define as categorias às quais eu app faz
parte, se ele é um app ou game, etc.
● Outros: mais abaixo tem uma série de coisas “chatas” porém
importantes, como classificação etária do app (tem de responder um
questionário para gerar), detalhes do contato do criador do app (você) e
a política de privacidade do app, que você pode marcar um checkbox
avisando que não tem uma.
Depois de preencher todos esses detalhes é possível clicar no botão de
“Salvar rascunho” que fica no topo da tela à direita. Na verdade recomendo
salvar caso tenha de terminar depois.
A próxima etapa é definir o “Preço e distribuição”, no menu correspondente
na direita. Aqui você define o preço do seu app (caso não seja gratuito), os
países em que ele aparecerá para download, se ele contém ou não anúncios e
mais alguns checkboxes no final referente à algumas leis. Se não marcar,
obviamente não consegue publicar o app, então não há muito o que fazer
aqui.
Com todos esses itens prontos (ufa!) seu app está pronto para publicação. Na
verdade, tão logo ele esteja, o botão de publicação vai ficar habilitado no
canto superior direito do console de desenvolvedor. Enquanto não estiver
habilitado, revise todos os menus à esquerda (eles mostram inclusive um
check verde quando foram finalizados) procurando por campos obrigatórios
que possam não ter sido preenchidos.
Depois de publicado o app pode demorar até alguns dias para ser aprovado,
embora o mais comum seja algumas horas. Enquanto isso ficará uma
mensagem de “Processando atualização” no canto superior direito e não há
mais muito o que fazer. Esse processo se repetirá toda vez que for lançar uma
nova versão do seu app, embora a burocracia apenas terá de ser revisada ao
invés de preenchida do início ao fim.
Política de Privacidade
Uma notícia recente do Google criou alarde entre a comunidade de
desenvolvedores e usuários Android: o Google pode em breve excluir
milhares de aplicativos da Play Store!

https://www.tecmundo.com.br/play-store/114174-google-excluir-milhares-
aplicativos-play-store.htm

Isso se deve a um movimento da gigante das buscas em prol de melhorar a


qualidade dos apps presentes na loja e não deveria ser um grande problema
caso um dos requisitos do que eles consideram “apps de qualidade” não fosse
algo geralmente negligenciado pelos desenvolvedores: a Política de
Privacidade (Privacy Policy).

Não apenas a ausência de uma política de privacidade será a causa da


exclusão de diversos apps mas também a “falta de clareza” com que alguns
desenvolvedores relatam como usarão os dados coletados dos usuários de
seus apps.

Mas afinal, o que é uma Política de Privacidade?


Basicamente uma política de privacidade explica de que formas as
informações coletadas em seu site (ou app) são utilizadas.

Saliento ainda que é preciso esclarecer como esses dados serão utilizados e
para que finalidades e, ainda, se a empresa vai repassá-los para empresas
parceiras, por exemplo. Esses são pontos extremamente delicados em apps
que fazem acesso de dados pessoais dos usuários e/ou apps que sejam muito
famosos, que lidam com as informações de muitos usuários.

Uma boa política de privacidade deve ser clara, objetiva e demonstrar com
transparência quais dados o seu app coleta e como as utiliza, quem tem
acesso à elas. Embora não exista um modelo formal para elaboração desse
“documento”, as perguntas abaixo podem lhe ajudar na construção do
mesmo:
● quais dados o seu app coleta dos usuários? Nome? Email?
Localização?
● esses dados que seu app coleta, são armazenados no app ou na nuvem?
Se sim, onde?
● esses dados que seu app armazena, por que são armazenados? Em que
circunstâncias são usados novamente? O usuário será requisitado a
confirmar novamente o uso de tais dados?
● em casos como dados de contato, com que frequência o usuário será
contatado através deles?
● o usuário pode utilizar o app sem ter os dados coletados? Se sim,
existe alguma limitação/restrição de uso?
● você (dono do app) compartilha os dados dos seus usuários com
outros apps/empresas?
● o seu app coleta dados do usuário provenientes de outros apps? Em
caso positivo, quais dados e de quais apps?
● a política de privacidade é atualizada com frequência? Se sim, por
qual canal o usuário fica sabendo das atualizações?
● em caso de dúvidas sobre a política de privacidade, qual é o email ou
telefone que o usuário possa entrar em contato para conversar?

Como o Google está exigindo não apenas a existência de uma política de


privacidade, coisa que 28% dos apps mais populares omite
(http://veja.abril.com.br/tecnologia/28-dos-apps-mais-populares-omitem-
politica-de-privacidade/ ), mas também que ela seja clara, sugiro as seguintes
perguntas adicionais para quem realiza pagamentos por dentro do app:

● quais os padrões de segurança utilizados nas transações?


● quais informações financeiras são armazenadas no telefone?
● quais informações financeiras são armazenadas na nuvem?

Note que pode parecer assustador apresentar todas essas informações ao


usuário, que ele pode se sentir intimidado. No entanto, é imprescindível que
se você está construindo um negócio ao redor do seu app ou que ele
representa parte do seu negócio, a política de privacidade DEVE ser levada à
sério!
O que uma política de privacidade ausente ou ruim implica para os
usuários?
Muitos apps solicitam acesso à dados sensíveis do usuário ou a recursos de
hardware específicos, até mesmo sem a real necessidade. Essas autorizações
que o usuário dá ao app, muitas vezes sem ler, tornam o seu celular um alvo
fácil para coleta de dados como cartões de crédito, emails, dados pessoais,
senhas e até mesmo arquivos pessoais como fotos e vídeos.

A ausência de informações claras a respeito da real necessidade de se ler e/ou


armazenar tais dados dos usuários torna a coleta e venda de informações,
assim como o uso indevido das mesmas, um grande mercado para os
desenvolvedores maliciosos.

Instalar um app sem política de privacidade e sem ler as permissões que ele
solicita é dar uma “carta-branca” para que seu smartphone e,
consequentemente, seus dados, sejam usados da maneira que o dono do app
bem entender. E isso pode ser um grave erro, com consequências semelhantes
às dos casos Carolina Dieckmann, Stênio Garcia e mais recentemente do
Paulo Zulu. Todos tendo fotos pessoais roubadas digitalmente e
posteriormente divulgadas na Internet.

Mas o que fazer se eu sou um desenvolvedor de app idôneo?

Primeiro, você deve se certificar de que possui uma política de privacidade


online, disponível à todos os seus usuários até o dia 15 de março de 2017.
Esse foi o prazo máximo dado pelo Google a partir dessa data, qualquer app
que não tenha uma política ou que ela seja ruim, será removido ou ao menos
escondido das buscas na Play Store, dependendo do caso.

Caso não tenha uma, crie-a com base nas perguntas que elaboramos para
você na seção “O que é uma política de privacidade”, logo acima.

Caso já tenha um site para o seu app/empresa, inclua essa política em uma
página do mesmo em uma URL que seja pública (i.e. que não exija
autenticação). Essa URL pública deve ser informada no cadastro do seu app
na Google Play, que você pode acessar pelo Google Play Developer Console
em https://play.google.com/apps/publish/. O campo da URL da política de
privacidade fica no final da área de Detalhes do App, como mostra a imagem
abaixo:

Outro item mencionado pelo Google à respeito é que os chamados “apps


zumbis” também serão excluídos. Um app zumbi é um app que não é
atualizado há anos e que nem mesmo funciona nas versões mais recentes do
Android, implicando apenas em mais apps inúteis disponíveis na loja para
confundir os usuários. Se é o caso de algum de seus apps, certifique-se de
atualizá-lo o quanto antes também.
Seguindo em frente
Este livro termina aqui.
Pois é, certamente você está agora com uma vontade louca de aprender mais
e criar apps incríveis que resolvam problemas das empresas e de quebra que o
deixem cheio de dinheiro na conta bancária, não é mesmo?
Este livro é propositalmente pequeno, com menos de 200 páginas. Como
professor, costumo dividir o aprendizado de alguma tecnologia (como
Android) em duas grandes etapas: aprender o básico e executar o que foi
aprendido no mercado, para alcançar os níveis intermediários e avançados.
Acho que este livro atende bem ao primeiro requisito, mas o segundo só
depende de você.
De nada adianta saber muita teoria se você não aplicar ela. Então agora que
terminou de ler este livro, inicie hoje mesmo (não importa se for tarde) um
projeto de app para ser vendido para uma empresa ou que atenda a um nicho
de profissionais liberais. Caso não tenha nenhuma ideia, cadastre-se agora
mesmo nas plataformas de freelancers que mencionei na primeira seção deste
capítulo. Mesmo que não ganhe muito dinheiro sem seus primeiros projetos,
somente chegarão os projetos grandes, bem pagos e realmente interessantes
depois que você tiver experiência.
Me despeço de você leitor com uma sensação de dever cumprido. Caso ache
que tenha faltado algo neste guia, ou tenha alguma sugestão, ideia, crítica,
elogio ou apenas queira bater um papo sobre computação móvel e projetos de
apps, me procure em meu blog http://www.luiztools.com.br
Caso tenha gostado do material, indique esse livro a um amigo que também
deseja aprender a desenvolver apps. Não tenha medo da concorrência e
abrace a ideia de ter um sócio que possa lhe ajudar nos projetos.
Um abraço e até a próxima!
Curtiu este livro?
Deixe seu feedback lá na Amazon para ajudar este humilde autor:
https://www.amazon.com.br/review/create-review/ref=cm_cr_dp_wrt_top?
ie=UTF8&asin=B01IJQ0AU6&channel=detail-
glance&nodeID=5308307011&store=digital-text
Aproveita e também me segue nas redes sociais:
Facebook: http://fb.com/luiztools
Twitter: http://twitter.com/luiztools
LinkedIn: https://linkedin.com/in/luizfduartejr
Conheça meus outros livros:

Programação Web com Node.js


Node.js e Microservices
Scrum e Métodos Ágeis: Um Guia Prático
Agile Coaching: Um Guia Prático
Java para Iniciantes
MongoDB para Iniciantes

Conheça meus cursos:

Curso online de Scrum e Métodos Ágeis


Curso online de Node.js e MongoDB

Você também pode gostar