Escolar Documentos
Profissional Documentos
Cultura Documentos
41 Edio
Ricardo R. Lecheta
Novatec
. ~ . _ . ~ )2/1998.
Copyright ? 2009, 2010. ZOIS. 2015 da Ntwatu l.liIOIl lldl
~ , . t _ pj ~ mt 'idos ela Let 9.610 ele I9/( l _ , ` ` _ _
'ludus txs LIIITIIUS n\t.t`\ lt .\`( P ` P pk' Or m|qu.- P|'0Q$$(), Scm prvll klUIUl'IZ:Igl0. POI' LSLFIIO,
pmibida ii reproduo desta obra. mesmo parul . P CI
do autor e da liditora.
ISBN: 978-85-757.2-440-3
Histrico de impresses:
Agradecimentos ........
4
10.5 Agendando tarefas contnuas na activity ..................................... ...... 333
'i121.6 Implementao de um tela Splash Screen para sua aplicao ........ ...... 335
10.7 AsyncTasl< ............................................................................. ...... 337
10.8 Download de imagens com a biblioteca Picasso ...... 342
0.9 Links teis ..........................................................
.
344
1.11 Animaes com item compartilhado entre duas activities ...... ..... 3 78
.12 Cmpatibilidade com verses anteriores ........................... ...... 78
7.13 Links teis .............................................. ................... ----
........380
Captulo 12 | Toolbar ................ ---
12.1 Introduo Toolbar ...................................... ---~-- :
12.2 Utilizando a Toolbar como a action bar ................ ...... 8 6
12.3 Utilizando a API da Toolbar (modo standalone) ....... ------ 3
12.4 Links teis ............................_................._......... ...... 3 88
.................
}3.12 Criando os fragments do projeto ............. _____,_ 4 18
_3_3 Links teis ...................................... _______ 4 21
ayer ....................
%;;*2.;f;*.1;;;*..:* e `'''''~
~~~8
;;gf1z~m.zizy@..i......z _..._._... iiziiiw" ~~ Sw
- uma
21.5 intent
do fico eYo cvieonativo
plzVideo\/iew
Cm 0 Cl21SSC er (1 . ........
'd.....
"'`''''.593
_.5 89 . 595
1
600
604
607
610
.612
.62
12 Links teis .................................... . 614
615
22.1 Introduo ............................................. 615
22.2 Google Maps Android API - Verso 2 ........ 616
223 Google Play Services .............................. 616
22.4 Gerando a chave de acesso dos mapas ....... . 617
.........646
Captulo 23 I Google Play Services e localizao .......
646
23.1 Introduo .........................................
647
23.2 My Location ...................................................
233 Monitorando o GPS ( moda antiga) ................ 647
23.4 Monitorando o GPS (Fused Location Provider) . 649
65 0
23.5 Conectando-se ao Google Play Services ............
. 651
23.6 Obtendo a ltima localizao de forma eciente
655
23.7 API de localizao do Google Play Ser* ices .......
659
23.8 Desenhando uma rota entre dois pontos ....... 661
ouuo
.. ........ 814
........821
812
..........899
Captulo 36 - Bluetooth ........................................................
36.1 Vericando se o dispositivo suporta Bluetooth ...... 899
36.2 Ativando o Bluetooth por programao ............. 900
363 Listando os dispositivos pareados ........................ 902
36.4 Buscar novos dispositivos Bluetooth ........................ ........ 902
36.5 Deixando o Bluetooth visvel para ser encontrado ........ ........ 906
36.6 Criando um BluetoothDevice pelo endereo ......... 907
36.7 Chat em Bluetooth ........................................... 908
36.8 Conectando-se ao Bluetooth pela serial .......... .9_8
36.9 Links teis .......................................... .9L9
..........920
Captulo 37 u Reconhecimento de voz ............
920
371 Introduo .....................................................
372 Hello TTS - faa seu Android falar .................... .........921
373 Vericando o idioma e falando em portugus ........ 927
929
37.4 Reconhecimento de voz por intent ..................
932
375 Reconhecimento de voz por um listener ......
376 Links teis .............................................. 935
..........936
Captulo 38 u Gradle ........
936
38.1 Introduo ..............................
937
38.2 Gerenciando dependncias ........
383 Trabalhando com mdulos ........ 938
38.4 Trabalhando com bibliotecas .................................. ........ 941
........946
38.5 Criando uma biblioteca ................................................. ........ 941
38.6 Configurando um servidor Maven Sonatype Nexus
38.7 Publicando no Maven Central .................................... ........ 9 46
38.8 Flavors ................................... ........947
38.9 Classe BuildCong ....... .........951
20
. ............... 953
38.10 Assinando o aplicativo para o build release ...... .. 955
Google Android - 4 ed
Este livro no teria acontecido sem a ajuda de diversas pessoas, algumas pela
contribuio tcnica e outras pela motivao.
Agradeo a todo o pessoal da Wasys,Tivit e Livetouch pelos incrveis projetos de
mobilidade, e a todo o pessoal tcnico da equipe pelas ideias e sugestes.
Agradeo a toda a comunidade Android pelo feedback e incentivo para continuar
escrevendo e lanar esta nova edio.
Em especial, agradeo ao Rubens Prates, editor da Novatec, por toda a calma e
orientao em todas as etapas da produo deste livro. Seus conselhos foram
fundamentais para tudo isso acontecer.
Agradeo Ana Carolina Prates, da Novatec, por todo o apoio de sempre.
Por ltimo, agradeo a voc pelo interesse em ler esta obra. Espero que a leitura
seja simples e empolgante.
21
Sobre o autor
22
Prefcio
23
24 Google Android - 4' edio
No nal ainda temos captulos especficos stwbre o novo sistema de builds do
Android (Cradle). desenvolvimento de aplicativos para relogios (Android Wear)
e corno publicar um aplicativo no Google Play
Espero que a leitura deste livro seja simples e ao mesmo tempo empolgante, que
os exemplos e explicaes possam l ief *er uma base slida para desenvolver
ornee
timas aplicaes e que voc possa aproveitar ao mximo o mercado de mobile,
que no para de crescer e est sempre procura de bons prossionais.
Ricardo Lecheta
http://facebook.com/ricardolecheta
https://pl us. googl c. com/ +RicardoLecheta
https://twitter com/rlecheta
` cAPruLo
Introduo ao Android
\___
1.1 Introduo
Nos dias de hoje, ningum consegue car longe de um celular, seja para mandar
um email, tirar uma foto, assistir um vdeo, conversar com os amigos, navegar
na internet, acompanhar as redes sociais etc. Portanto, os smartphones e tablets
atualmente so objetos praticamente inseparveis da maioria das pessoas.
Segundo pesquisas, mais de 3 bilhes de pessoas tm um telefone celular, e o
mercado de aplicativos virou uma febre, rendendo bilhes todos os anos.
Nesse mercado competitivo, temos vrios lados da moeda. Os usurios comuns
buscam um celular com um visual elegante, moderno, de fcil navegao, assim
como uma innidade de aplicativos e recursos. Tanto as empresas quanto os desen
volvedores buscam uma plataforma moderna e gil para desenvolver aplicativos.
Os fabricantes (LG, Motorola, Samsung, HTC, Intel, Sony etc.) precisam de uma
plataforma robusta e rica em funcionalidades para lanar no mercado os seus
produtos. E aqui onde o Android se encaixa, pois ele perfeito para todos os casos.
O Android o sistema operacional mvel do Google e atualmente lder mundial
nesse segmento. No entanto, o sucesso do Android no se deve apenas fora do
Google - por trs do desenvolvimento de toda a plataforma esto gigantes do
mercado de mobilidade, como fabricantes de celulares e operadoras. Esse grupo
que ajuda no desenvolvimento da plataforma chamado de OI-IA (Open Handset
Alliance) e conta com nomes de peso como Intel, Samsung, LG, Motorola, Sony
Ericsson, HTC, Sprint Nextel, ASUS, Acer, Dell, Garmin etc. Existe todo um
ecossistema interessado no desenvolvimento de uma plataforma mvel poderosa e
flexvel, de cdigo-aberto e que atenda s necessidades de todos. Embora o Google
represente grande parte da fora do Android, com certeza a plataforma est hoje
onde est devido ajuda de outras potncias do mercado mvel.
25
Google Android - 4 d
26
' d' vel vara diversas plataformas, como smarfplwnes
Atualmente oAndroid esta 1SPon } _ ~ , . 1 S (Goflgle Glass), carros
6 tablezg, TV (Google TV), relogios (Android,Wear),.ocu
mais utilizado no mundo.
. . zi '- _ 0
eracional movel
aseusseu
(Android Auto) e o sistema op ' bm est ca da vez mais utilizando 0
Vale lembrar que o mercado corporativo tam 0 rca es mveis
mobile, tanto que diversas empresas esto buscando incorpoflf ali l ,veis com
sis e - - _
da a dia ara agilizar seus negocios e integrar as ap ICHOS m
` t mas d) back end Empresas obviamente ortante
visam oespao
lucro; por
em isso, tar1IO
um mund0
05 smartphones quanto os tablets ocupam um imp u
em que a palavra mobilidade est cada vez mais conhecida.
Dentro desse contexto, estamos diante de uma excelente oportunidade, pois o mobile
um grande pilar na rea de tecnologiaisso
e segundo pesquisas
voc no e uma
pode car foradas areas que
dessa.
mais vai crescer nos prximos anos, por
O objetivo deste livro explicar o desenvolvimento de aplicativos para Android,
do bsico ao avanado, com diversos exemplos prticos e dicas de que voce vai
precisar no dia a dia.
A._7
1.2 Open Handset Alliance e o Android
A Open Handset Alliance (OHA) um grupo formado por gigantes do merca
do de telefonia de celulares liderados pelo Google. Entre alguns integrantes do
grupo esto nomes consagrados como Intel, HTC, LG, Motorola, Samsung, Sony
Ericsson, Toshiba, HTC, Huawei, Sprint Nextel, China Mobile, T-Mobile, ASUS,
Acer, Dell, Garmin e muito mais.
Quando 651 livro f0i SCfif0, O grupo era formado por 84 integrantes de peso
e voce pode verificar a lista completa e atualizada em: wwwopenhandsetalliance.
1 e da OHA existe uma otima descriao do que e essa aliana. O texrg ggt
um d - .
em ingls e vou apenas traduzir uma breve citao aqui. Hoje, existem 1,5 bilho
de aparelhos de televiso em uso em todo o mundo e 1 bilho de pessoas tm
lar tornando o a arelho One Ce u
acesso internet. No entanto, quase 3 bilhes de pessoas tm um telef 1
, . or ' '
mllndo Dessa folma .OS produtos de consumo mais bem-sucedidos do
pessoas
de inmeras u arem
superior
ecno o ` ' - _
todo melhoraria
o mu . a vida
formado por empresas lderes emt ndp. A Open Handset Alliance um grupo
para muar xpaC er..movel
iencia . , gldemovel
todos osque compartilham essa viso
consumidores
Assim, o objetivo do grupo denir uma lataf
lares para deixar os consumidores mais s-
is eiftos
_ ma unica
com e abertanal.
o produto paraOutrg
celu
Captulo 1 i Introduo ao Android 27
objetivo principal dessa aliana criar uma plataforma moderna e exvel para
o desenvolvimento de aplicaes coorporativas. O resultado dessa unio, como
voc j deve saber, foi o nascimento do Android.
Todos acabam se beneficiando com os avanos alcanados pelo grupo OHA e a
plataforma do Android: os fabricantes de celulares, os usurios comuns e, claro,
as empresas em geral e os desenvolvedores de aplicaes.
Os usurios de celulares so extremamente favorecidos com tudo isso. Hoje em
dia, todos querem um celular com um bom visual, de fcil usabilidade, com
tela touch screen, cmera, msicas, jogos, GPS, acesso internet e muito mais,
e o celular cada vez mais ocupa um espao importante na vida das pessoas. O
Android foi criado justamente para agradar esses usurios, possibilitando que
encontrem todos os recursos esperados em apenas um aparelho. O mundo da
tecnologia est sempre em evoluo, e a OHA tem como objetivo principal manter
uma plataforma-padro na qual todas as novas tendncias do mercado estejam
englobadas em uma nica soluo.
Para os fabricantes de celulares, o fato de existir uma plataforma nica e consoli
dada uma grande vantagem para criar novos aparelhos. A grande vantagem para
eles que a plataforma tambm livre e de cdigo aberto. A licena do Android
flexvel e permite que cada fabricante possa realizar alteraes no cdigo-fonte
para customizar seus produtos, e, o melhor de tudo, sem necessidade de com
partilhar essas alteraes com ningum. O Android tambm free' portanto, os
fabricantes podem usar e abusar dele sem precisar pagar por isso.
O fato de o Android ser de cdigo aberto contribui muito para seu aperfeioamen
to, uma vez que desenvolvedores de todos os lugares do mundo podem contribuir
para seu cdigo-fonte, adicionando novas funcionalidades ou simplesmente
corrigindo falhas.
j os desenvolvedores de aplicaes podem desfrutar de uma plataforma de de
senvolvimento moderna com diversos recursos incrveis, com tudo o que h de
mais moderno. Este o tema deste livro: o desenvolvimento de aplicaes com o
Android. E aqui voc vai entender o porqu de toda essa revoluo.
Al uns deles
~ - 'bir odem
uma te a para 0cxi
uSU_ .A_.
Cada aplicativo no Android disparq um novo primo e Outros podem car em
.g- qo em gundo plano por tempo indeterminado. Diversos processos eapll
Uicwg P AL
cativos os simultaneamente,
podem ser executade o kernel
I _do sistema operacional
o responsvel por realilf odo o controle de mem0fl21
, . z - decidir encerrar al um
Caso necessario, o proprio sistema oper8Cl0U3l Pf>d<{ _ _ g O
. _ , z - a vez
processo para libera r memria e recursos, e talvez ate reiniciar o mesmo proCeSS
d situa
posteriormente quan o a o
estiver controlada.
Toda a segurana do Android baseada na segurana do Linux. No Android,
cada aplicaao e executada em um unico processoe cada processo porlsu .
contm um a thread dedicada. Para cada aplicao instalada no celular e criado
um usurio no sistema operacional para ter acesso a sua estrutura de diretrios.
Dessa forma, nenhum outro usurio pode ter acesso a essa aplicaao.
Para os fabricantes de celulares, isso tambm uma grande vantagem, uma vez que
e possivel utilizar o sistema operacional do Android em seus celulares sem ter de
pagar por isso. Alm disso, a licena Apache Software Foundation (ASF) permite
que alteraes sejam efetuadas no cdigo-fonte para criar produtos customizados
sem precisar compartilhar as alteraes com ningum.
S ,
-,.m _ .
1.5 Mquina virtual Dalvik
Provavelmente voc '
J abs que
aplicaes para o Android. 3 lmgugem Java e utilizada para construir as
O fato
uma mquina virtualjava
. a verdade o ueUVM)
e que em seu sistema operacionalN
temos u ' '5' E
no exi t
chamada Dalvik que e otimizada , ~ q . . _ a maquma vlrtual
para XPCUGO em dispositivos mveis.
eAotodos
desenvolver as aplicaes para o Android voc v - .l. _
os seus recursos 2norm
m 1 ' _al uu lzar 3 linguagem Java
ente, mas depois que o bytecode (.c1ass)
Captulo 1 in Introduo ao Android 29
compilado ele convertido para o formato .dex (Dalvik Executable), que representa
a aplicao do Android compilada.
Depois disso, os arquivos .dex e outros recursos como imagens so compactados
em um nico arquivo com a extenso .apk (Android Package File), que representa
a aplicao final, pronta para ser distribuda e instalada. Ao utilizar o ambiente de
desenvolvimento do Android Studio, toda essa compilao e gerao do arquivo
.aple ocorre automaticamente, portanto, no preciso se preocupar com isso.
Atualmente, o sistema de build utilizado o Gradle, o qual independente do
Android Studio e pode ser executado separadamente. Portanto, voc pode com
pilar todo o cdigo por linha de comando se necessrio.
claro ` . _
em segundo plano consigam executar sem atrapalhar a atividade do usurio
enquanto ele est acessando a internet ou atendendo uma ligao. 3
_ I qfl 020 podemos nos esquecer dos recursos visuais e todas as APIs
_ er abertoque
e totalmente customizado
disponiveis, mas o fato de o Android s '
diferencial vale a pena ressaltar 6 um
1.10 T-Mobile G1
O T-Mobile G1 desenvolvido pela HTC foi o primeiro celular lanado com a
plataforma do Android e, como esperado, agitou o mercado. A notcia_de seu
\CI
Google Android - 4 d
32
. _ - ~ s ex vectativas de vendas da
lanamento causou um grande impacto e superll 21 1 ddos de pr
HTC: mesmo antes de seu lanamento, todo o estoque para os p
-venda j havia sido esgotad.
a ser vendidos nos Estados UnidoS
Os primeiros celularesS$HTC G1 comearam , _
179. Um fato interessante e que eu termml
no dia 22 de outubro de 2008 por U
G1 l n ado e fiz todos meus estudos
a 1" ediao deste livro antes mesmo de o ser a _ ' _
1 curso de Android que 11lSU`l
somente utilizando o emulador. Lembro que no
alguns alunos tinham comprado o G1 e vieram mf! ITIOSUQC F01 @m0C10"3me'
-._~1e
1.12 Um pouco sobre a histria e verses do Android
A verso 1.0 do Android chegou no mercado em 2008 com o famoso 'l` Mob`l
. - ~ ersao
c.,._ea e `
Gl, 6 depois o Android nao parou mais de evoluir,
http://developer android.com/about/versions/android-1.5-highlights.html
~_I. -1
, 'mw ~f
No Android 1.6 foram criadas as medidas de densidade (ldpi, mdpi, WCP) que Vamos
estudar ao longo do livro, pois foi quando o Android passou a ser utillilzadodpo;
dispositivos de diversas resoluoes e tamanhos de tela. O Android estava c g rcado.
um novo patamar e comeando a ser amplamente utilizado e adotado pelo f1'l
Fontes:
http://developer android.com/about/versions/android-1.6.html
Fontes:
hPf//developerandroid.com/about/versions/android2_O_ html
http://developerandroid.com/about/versions/android-2.0-highligh[5 hzml
'1P=//dfvelpfrandfoid. com/about/versions/android-2.1. html
Captulo 1 I Introduo ao Android 35
1.16 Android 2.2 (Froyo)
5
Lanado em maio de 2010, o Froyo (Figura 1.4) trouxe diversas melhorias de de
sempenho para o sistema operacional, como o compilador JIT Uust In Time) e
uma engine de JavaScript mais rpida.
` ,.'' 'i
Nessa verso foram adicionados recursos clssicos como o USB Tethering e Wi~Fi
Hotspot, assim como o suporte ao Flash.
Fontes:
http://developer aridroid.com/about/versions/android-2.2-highlights.html
hit p://de vel open aridmid. ;om/ahut/ versions/and roid-2.3 -hi ghli ghts. html
http://developer:android.com/about/versions/android-3.0. html
http://developer android.com/about/versions/android-4.0-highlights.html
i,. E
'\\
com relao ao desempenho do sistema, e todo o sistema operacional ganhou me
V\'~
'
lhorias no suporte s animaes, deixando a interface mais sensvel ao toque e fluida.
`= ..,`J .` ''
' ie.
~. ;?``
p, -...,., * g._5 _.
.x;._4_ -zzigg . ,Y-,.z,_
=. W
Q f ;>~,~E~<f
_ ifz -*
~:> -z.
EQ*
`~'
.,.. ,,. _ _,
' .;'- fff .~
muitos detalhes.
Hmtc
Iztf['//d'v1]er:mdroid.umr/about/vcrsin5/cuybcan'html
http://developerandroid.com/about/versions/kitkat. html
lc'
Figura 1.10 - Android 5.0 (Lollipop).
Nasceu ento o Material Design, que um guia completo sobre como implementar
o visual, animaes e a interao entre os componentes de um layout, levando
em considerao que o Android se tornou uma plataforma comum para vrios
dispositivos, como smartphones, tablets (Android), Wearables (Android Wear),
culos (Google Glass), TVs (Android TV) e carros (Android Auto).
Isso o mais importante, uma vez que as tcnicas do Material Design no precisam
ser implementadas somente nos smartphones e tablets, pois o Google criou um
padro de design consistente entre vrias plataformas, como mobile, Web, Wear etc.
Dentre outras melhorias, tivemos as noticaes, que agora tambm aparecem
na tela de bloqueio (Lock Screen), e as head~up notications, que aparecem no
topo da tela com alta prioridade. Um exemplo de head~up notications a ligao
que permite atender ou rejeitar uma ligao telefnica diretamente na noticao.
Antigamente, esse recurso no existia e a aplicao da ligao mostrava uma tela
cheia para o usurio decidir se atende ou no a ligao.
Outra novidade interessante foi o projeto Volta, que trouxe ferramentas para
auxiliar a anlise do uso da bateria nos aplicativos. Tambm foi modicada a tela
de aplicativos recentes (Overview Screen), que mostra as ltimas tarefas que esto
sendo executadas, sendo que um aplicativo pode conter uma ou mais tarefas. Foi
criada uma API para os desenvolvedores controlarem esse comportamento. O
Lollipop tambm suporta a OpenGL ES 3.1, trazendo um desempenho superior
nos jogos 2D e 3D.
A plataforma do Android est chegando a outro patamar, e o Google TV tambm
recebeu um grande release. Foi criada a API Leanback para criar interfaces ricas
para TV e o TIF (Android TV Input Framework).
Novamente, so tantas as novidades que recomendo olhar a documentao oficial.
Fonte: http://developer android.com/about/versions/lollipop.html
Google Android - 4 edi
_ , , _ ve
40
. . . - ze
_ z ' e outros.
Google est seguindo essa nomenclatura agora. Porem, oficialmente a nowa mo
do Android deve ser lanada entre outubro e novembro de O15, e somente 6
o esta versao comea com a letra
saberemos qual o novo sabor do Android. Com
M, algumas das suspeitas sao os famosos doces MGIM S, MCHIOS, dmf
Dentre as melhorias j anunciadas f r
no Android M, temos um leitor de imprSS0
digital com sensor biometrico e o Android Pa); uma plataforma aberta para 2
pagamentos por meio de cartes, internet e NFC.
Uma das funcionalidades que achei mais interessante o novo sistema de con
trole de permisses dos aplicativos, o qual possibilitar que 0 usurio conceda a
permisso individualmente. Ser possvel optar por dar acesso cmera enquanto
nega-se a permisso para acessar o GPS, por exemplo.
Outra funcionalidade do Android M muito comentada so os App Links, que
permitem que determinados aplicativos sejam escolhidos como padro ao abrir
links de determinado domnio. Exemplos clssicos so os apps do Tvvitter, Google
+ e Drive, e nesse caso qualquer link desses domnios pode ser aberto diretamente
em seus respectivos aplicativos sem a necessidade de perguntar ao usurio qual
aplicativo deve ser escolhido.
O Google Now tambm recebeu atualizaes e agora ele pode ser chamado
diretamente da tela de bloqueio, pela opo no canto esquerdo inferior da tela.
Enm, agora esperar pela nova verso do Android. No entanto, como desenvol
vedor, voc vai perceber que no Android SDK possvel baixar a verso prvia do
Android M e j ir brincando com o emulador.
Neste livro, vamos aprender a criar aplicativos para Android. Na maioria das
vezes, vou tentar manter a compatibilidade com as vers es antigas, at porque 0
bilidad ~ - . . P
que iremos estudar so os conceitos principais do Android
desenvolvimento.
Este captulo aborda a instalao do SDK e a congurao do ambiente de
desenvolvimento no Android Studio. Para validar a configuraao do ambiente,
criaremos um simples projeto para testar o emulador.
O Android SDK pode ser encontrado neste endereo: http://developer android com/sdk/.
Neste livro vamos utilizar o Android Studio para desenvolver aplicaes para
Android. No se .preocupe com o SDK neste momento, pois vamos baixar o
Android Studio e ele j contm o SDK.
O Android S ` " . .
2.2 Requisitos de software e sistema
http://developenrzndroid.com/guide/topics/manifcst/uses-sdla-clemcnt. html#ApiLevcls
_ nci G 1 , . . ,
Neste livro vamos adotar o Android Studio, que a IDE oficial de dcsenvolv`
para Android. O Android Studio foi anu ` d .I z lmemo
no Imelhj [DEA da JetBminS' a o no oog e I/O 2013 L c baseado
~ . ~ portantes se com 'ir A - ~
O Android
5111410 apresenta alguns diferenciais im
ECIIPSC, que antigamente era a ferramenta ocial pt ado do
Captulo 2 n Congurao do ambiente de desenvolvimento 45
1. Editor visual mais uido e com mais opes.
2. Sistema de build mais moderno baseado em Gradle (gradlaorg).
3. Diversas utilidades e facilidades ao desenvolver para Android, sendo muito
integrado ao Android SDK.
4. Templates de projetos para smartphones, tablets, relgios etc.
5. Atualizaes e melhorias frequentes.
Uma grande diferena entre o Eclipse e o Android Studio o processo de com
pilao dos projetos. No Eclipse cada projeto compilado do jeito clssico, como
qualquer projeto java dentro do Eclipse. Mas no Android Studio a compilao
feita pelo Gradle, que um moderno sistema de builds. Segundo o site oficial do
Cradle (gradlenorg), ele denido com a seguinte frase: Gradle combina o poder
e a flexibilidade do Ant com o gerenciamento de dependncia e convenes do
Maven, em uma maneira mais ecaz
Se voc no est acostumado com gerenciamento de dependncias, pode achar o
Gradle complicado no incio, mas que tranquilo que durante a leitura do livro
vamos praticar bastante. Com a prtica do dia a dia, voc vai se acostumar com
ele e aos poucos vai perceber suas vantagens.
Agora vamos colocar a mo na massa e baixar o Android Studio; acesse a seguinte
pgina, conforme a figura 2.1.
http://developer android.com/sdle/
NDK j
P g/:J
d dS d
K
Hip
E ld y
Spp tLb y
Fi
ADK
. .,,_..._.....e._,............. zz zi
Eclipse With ADT ' ~ Other Do.~.=nlrad Options
. Migralmg to Android Sludl
T ke a Survev
Nome
giadie
I lib
K license
L DWQW
tuiic|.m
uceusem
E uoricem
Q unmstallexe
(fg ` .\ _` `
.UM
, I?
. ..
gif [gq Y
Figura2.3-P' ._ .
rirnuru 'xiua do Android Studio
Captulo 2 I Congurao do ambiente de desenvolvimento 47
Antes de criar um projeto, recomendo clicar no boto (ongure do Wizard para
fazer o download e a instalao das plataformas (verses) do Android, embora o
Android Studio j venha com vrios itens pr-instalados. O wizard com as opes
de configuraes pode ser visto na gura 2.4. Nessa pgina do Wizard, clique no
link SDK Manager para abrir o utilitrio de instalao do Android SDK, a explicao
de como continuar est no prximo tpico.
Android Studio -- _
Welcome to Android Studio
Recentlrojects _, C fg
`lffDl' H H V S
An drordUtils _
4% Settings
E,
~...
Plugins
% Import Settings
[ Export Settings
i Project Default:
. z get e sim
Instalar verses da plataforma do Android e bibliotecas pelo QDK Mana ' `
ples:voc entender
basta selecionar
queosprecisa
oestar
pacotes ' 'instalado.
desejados e clicar cm Install packages. O importante
Para comear, sempre importante
_ . ua izat os. que so
manteros trs primeiros itens at l` l
referentes ao SDK Tools, pois isso influencia diretamente na com
Android SDK Tools - Ferramenta pilao do cdigo.
s do SDK, como o emulador.
SDK Platform - Esse o item mais importante, pois a plataforma dessa verso
do Android. No diretrio do SDK ele ser instalado na pasta /sdk/platforms,
a qual contm as classes e APIs dessa verso do Android.
Samples for SDK - Documentao do SDK.
EABI v7a System Image - Imagem do emulador do Android. Voc pode baixar
a verso da Intel X86 ou ARM.
Android TV System Image - Imagem para criar o emulador do Android TV Voc
pode baixar a verso da Intel x86 ou ARM.
Android Wear System Image -Imagem para criar o emulador do Android Wear.
Voc pode baixar a verso da Intel ou ARM. 1
Google APIs System Image - Esse item idntico imagem do emulador conven
cional, mas ainda contm as APIs do Google. Recomenda-se sempre criar
o emulador com o Google APIs.
Mais abaixo na pasta Extras deixei instaladas as seguintes bibliotecas.
Android Support Repository - Repositrio utilizado pelo sistema de build do
Android Studio (Gradle).
Android Support Library - Biblioteca de compatibilidade com suporte s verses
mais antigas do Android. Contm vrias classes que permitem criar aplica
tivos que funcionem de forma coerente em todas as verses do Android.
Google Play Services - Bibliotecas adicionais do Google como Mapas V2, Lo
calizao, Google Fit, Google Drive, GCM (Push) etc.
Google Repository- Repositrio interno utilizado pelo Google.
Google USB Driver - Driver para os smartphones e tablets da linha Nexus do
Google. Esse item necessrio apenas no Windows.
Intel x86 EmulatorAccelerator(HAXM instaIler) - Esse item um acelerador de veloci
dade do emulador para Windows. Depois de baix-lo, necessrio instal-lo.
Com isso possvel criar os emuladores x86 que so mais rpidos.
so Google Android - 4 ed
r(HAXMl
2.6 Intel Hardware Accelerated Execution Manage
A l ` l -
O emulador .do'pornc
' lentido
r'lceUSB
n' z _ . Y ou
mitos descnvolvedorCS
c lunoso sua
` ` ~- I' n.:m/).
optam por desenvolver diretamente com um dispositivo ical plugado na
instalar emuladores de tcrceii'oS.C0l11> 0 (.CHym0fl<> (l1llP>-//WWWKf'")'""l 'I
r nte!
precisar entrar na pasta /sdk/extras/intel e instalar o software (Figura 2.6), pois e
leito apenas o download do instalador.
1 1 ` `
_ ` Welcome to the |ntet(R) Hardware Accelcrated Exeeutlon Mana er Installer
I' l*"~i~ UL' UL r^'~~1l1'^P^~',*'`H^f-l*^>'z"<=^v-~vto|~~,t.:tIntaIfz-)HAXM- '~ .
Introduction
11!A1Ill1I1l1 ,\
wullliztlo
, _ `v_ 1.
Fl
i 5 ,. r>~.-z~.zz~re
Iptti\
KI
.x~-,.
TvdaInte
1 ~, que
gensx docmuladordoAndroid,/\ndroidWeareGooglc
aparecem no SDK Manager.
x86 Atom System8Ima
0fS, chamados de ~
Fsses emulad
que
sao os itens com o nome Intel
z 1 f*=*==~* S
Por padro, o Android Studio vai criar os projetos na pasta do usurio no sistema
operacional, no subdiretrio AndroidStudioProjects. Eu particularmente costumo
mudar esse local para outra pasta.
Clique em Next para continuar. Na prxima pgina do wizard, selecione o item
Phone andTabIet para criar um projeto compatvel com smartphones e tablets (Figura
2.8), em seguida selecione no campo Minimum SDK a API Level 21 (Android 5.0), ou a
maior verso que existir na poca que voc estiver lendo o livro. _
S2 u
i f . voz
_ L `_` .,`.) t\-\ V .{< ..
|w_'_` .L Y., .__ zw aew. :-\
_ -.z.;=-\l\W -' '
Google Androld - 4 d5
z-- <'*
. . _ . - x -~ - 1)`l`
Figmu 2.8 - (riando um H'Ol<`fU
Observe que no vvtzard tamlwem reinos opcoes para ttiat pro]LIU> W111 *UI * 1 *
ao Google 'l`\'. Android \\'ear treiogiosi e Google (iias \UL`Ui0S)
Importante: ao criar seu primeiro pioieto .f\ndnid. escolha a maior versao dispomvel
no campo Minimum SDK. assim os exemplos explicados nos proximos topicos vao
funcionar. Se voc selecionar alguma versao antiga do Android. o proieto sera
criado com a biblioteca de compatiiilidade. tuas isso cstudaremos somente depois.
Ada No Activitys
r
{'" ` W "
i
sunzazvuy
~ fadlfaurf
gumgmg 'H.ondroidS!udzo' Gradle project mic
Google Android - 4 @d
Cancel
.,..f
:-..
.~z.
zw A
"" mig ,n-un-
ez-
.. _.M
,:,,
F1 '
A gura 2.14 mostra o wizard para escolher o dispositivo que deve executar o pro
jeto. Neste caso ela est mostrando um smartphone Nexus 5 que est conectado
na USB. Agora basta clicar o boto OK para executar o projeto direto no dispositivo.
,Q Choose Device
__hoose a running device
aun:h emulator
z [none]
:ame device for future launches
M
Figura 2.14 - Selecione 0 dispositivo ou emulador
56
' ~ *f -f habilita-lo,\
f . ' . ~ , " _;
- - ' - ~~ tisoselccionai 1
I Q , `, mwP'
, ~ ' `( ` ' ) ` .
_ _ ~. 2 ,U;nerioi.laia ,
Numbe
P*l no Amlumlfi ( 3 sl c clicar sete vezes seguidas na oP%ao B QC um
_ _ _\ , (1mLI1S.1 L
About phone nas conllill" * ,, . ~ 1m informando qu 21210171 W
AO tum. IML Wu MMM um 0 tions eistar habilitado.
LiL`SL`ll\'UiVCLi()l` c o inenu Developer P *
_. Hello World..N05
._ , . gm a, ,mensagem
f se
Ylcxecutei
_ . . o,proieto
z dis ositivo concc
O iltado pode ser visto na gull 2-13 _ . . , tado na USB,
diretamente no P
m0lm Qu 3 d , Tizr e instalar a aplicao no emulador.
mas no prximo tpico vamos aprcn U I Q
Hello world'
rg.
Isks&Conteds U 6 ,;,
, , Generate Javaoc... 'i`""i'i"""f'f'f" l'`'
f' i; Save Project as Template...
Manage Project Templates...
1, Groovy Console...
-fr *fr "WW" f<f*'
O ooagiz cima Tools 5 Sm Project warn amam Fes
I Open Terminal... '' wi DE/C! M0nt0r A
E Avo Malaga
SDK Manager
Enable non imzgmion
To
Vuhnldevicesailowyouttestyowappcutinnwithouttuvingta
ownthephysicildevicsz
` whefeyouangetup-to~dteinform.ionm
which devcesareactivent|\eAndrodndGnogIePlayeosyst|m.
Ee-Mil
Figura 2.17 - Android Virtual Device Manager
.ii-i--__ _
S__.-.__... -..~--~
-l [ Nexus 3
wa
,,,, Nau; onz_N_s. 3. T' wm mp. Sun nmll
Run WW
Nul FW 0" lr .hm mp. Oomkv Mv!
t..
1 QZQ.
Figura 2.18 - Selecionando o tipo do dispositivo.
r 21
i ` ix Ari tem
i ^r:I
*' 5.0.1
ima dvuionduuyagir
'""9" , '7 ~ Su documontwon toi MGOG 5 AH;
l
Figura 2-19 - Criando um AVI).
(aptulo 2 I (ongurao do ambiente de desenvolvimento 59
Dica: o emulador ARM do Android bastante lento, por isso se puder crie
o emulador x86, que tem o acelerador da Intel. Caso seu computador no
suporte a tecnologia de virtualizao da Intel, recomendo instalar o emulador
do Genymotion (genymotioncom).
Na ltima pgina do wizard (Figura 2.20) digite um nome para o emulador e clique
em Finish. Opcionalmente verique as opes avanadas no item ShowAdvanced Settings.
Virtual Device Configuration '
r.. _. _ _
^' ""' U'5**..**''--_. -..W ..ss .. 2 2 lss. _ _ ---W --. ... Avn mm.
] uzzozs 4.o-aoozsoompa cnznglf..
Stmup size
and Sul: A_t
H I l TYIO IIBITIO ol U'|lS VD.
orientmon
ffnumfd
Perforrnance
Qi uzz Host seu
l:I Stovea snapshot for fasterst rt p
'i au na efther me Host GPU p h t
;"z.....".......iii"`;.....,.i`*i ':
z . l..5r+s..l
Figura 2.20 - Criando um AVD.
Depois de criar o AVD o resultado ser como a gura 2.21, que mostra a lista dos
emuladores criados. Para iniciar o emulador, basta selecion-lo na lista e clicar
no boto Run que um tringulo verde. Nessa lista voc tambm pode editar as
conguraes do emulador e at exclui-lo. Para criar mais um emulador com
outra congurao, utilize o boto (reateVirtuaI Device.
Google Android - 43 edi0
60
Avn Mzmaer i
AH M run:l''^"'i Ill MM
; uzwzsmai oooohvi Z' `9l'm 650
M8, .7Y
. 5g,54N.,;AP|,21
@~<=*
M3 WV-I" .z i zizif'
.,W...,,,, J H MF
-l ~I`Y ..^"`'}'\^\Q,. nf- 7 H
a'_~,q 4,1-=[,,.,z`JY_' J
'l`Y"\' J `"'i*1.:_; *' '1; my
n~:ri~ xJ'i'\'~l '11 Y li
`UL~2f! l\if2~iz,, w; ,TP
1 ni i 5-;~_zz,
`l 1 1..
l.. f-~=_.;__fl li t il
ma as co g _ d la
2.12 Entendendo um pouco mais sobre o emulador
l 'mula completamf
U d isas le ais do emulador do Android que e e si
o sistema operacional real, e possvel acessar a estrutura de arquivos o emu
dor. Dessa forma, podemos visualizar os arquivos apks das aplicaes instaladas,
mulador. Toda essa estrutura de
alm de excluir, enviar e recuperar arquivos do e '
diretrios do emulador pode ser visualizada pelo Android Studio ou por ml0
de um prompt de comandos.
Demonstraremos primeiro como acessar o console do Android pelo prompt de
comandos. Para isso, digite os seguintes comandos em um prompt (o emulador
precisa estar aberto):
C:\android-studio\sdk\piatforn-too1s>adb devices
List of devices attached
1 enuiator-5554 device
tm emulador
eseja acessar oestiver aberto
console. para
Nesse escolher
caso em qual
como temos a instncia do emulador voc
do _ . . .
A0 f3Zf1550 Um prompt ser aberto, permitindo navegar nos arquivos e pastas
. emulador. No exemplo a seguir, foi digitado o comando ls do Linux para
visualizar a estrutura de diretrios:
a.
basta digitar adb shell para acessar o ,console do pri:;)Sei1nnul:trd1d1rli(1(lisberto,
C:\android-studio\sdk\p1atform-tools>adb shell
#15
cache
init
etc
var
data
system
tmp
root
dev
Captulo 2 1 Congurao do ambiente de desenvolvimento 55
Observe que o sistema operacional do Android Linux. Dessa forma, ao entrar
no console do emulador, necessrio utilizar comandos do Linux, mesmo se
voc estiver desenvolvendo suas aplicaes no Windows. As aplicaes instaladas
cam na pasta /data/app. Vamos entrar nessa pasta para visualizar nossa primeira
aplicao desenvolvida.
# cd /data/app
# ls
br.livroandroid.helloandroidstudio-1.apk
#
z; )
:cz,an::o~dfamrhe: 551 8600 ~;>ca:he 2014-09-05 15
'e*:_ar,.:c kemail 1817 8602 Y z -config 2014439-05 15
:\:rn_af1drc.:l_$yStemu 442 32303 ...Y cf 2014439-05
arczd Dfmdi 414 86044 87...
nf.1.'.r:afafcz_h-oancirmd , 4 data
855 8505 anr2014-09433
2014439-031-$(
'CO'Ll'l&(DCl.3l'C1VCl8fC1lEf' 922 8605 K app* _* H W _ K V rf M 2G'I$_~U9-U5
z<.zf..zf..<:f<z<1.f.zz.g5 ras aos cz tzfifvzan1r<f.izi.zzzamuzzs-tape jsa zowolo 'is
a.m1ez:.pro:ess.cor 585 86139 C1 timwmandrcnd.heiIoan'drozdsiudioiest-1.ao U O 7 O 25160 201409-d
ccmaP.drod.iputmemod Ie 525 3610 v app-asec 21314-D9-63
core,amdrozrJ,ca!e*:ar 979 SEU z app-lib 2314-09-G5
com ridridieychazn 1059 8512 .if app-private 2B*f=4=Q9-03
:r:'nam:l'o.d.rnms 951 8614 ~ cz: ar1run-tests 2814-Di-17
*c~man13ro.ddcsi<cicck 1000 8616 '~ za backup 2014439-3
l .~ .nz:1fe.zi.phonzz 5413 8618 ___, tiugrepmts 201i5^;~fJ3
i, ceasz 390 8521 . dalvk;ache ZUH-W-U5
fzIL=:.r'~f Z " ' _
ZU9? 8601 .;~ dara 314B9'iI3 V
'- ........|.-,. tl :RA hu Il.:
Y" pf Java regexes, Wex mrii prai: app;t text: lrrzit scope. vecbcrse V H A
PID TID Application Tag Text ^
.. _..
'A_.. ......,_,.
` 'WT C": fz--*xr V *s_~_ -.r Iz7x:i.r. : r i ;l":' 'x"2 r '
~
. _ ~. .'_ -..-'I
1" _ .__ ~z.z- ._, ..
ow of 492.M
_, , . . ueas 1ntlisF|le
.z ~' Exporer
einclusiv tirar um screenshot da ttla.\/ale lunbritq `_ f cleimmduImjmcln
(lggg) Q as outras vo mostrar as in lormaoes do .lisP5'V* *
Devices, caso exista mais de Um
. _ . . ,. .. . ' cvicc Monitor'
.- n roit tu to com ~s 1
. , ane a.
Dica: no Windows tive problemas para uiccutrso /tlndroid Dpcnnigso d
somente funcionou ao executar o A, Adav c.Io:Gin
administrador. Essa ferramenta tambem pode Scr executa P
C:\androd-studo\sdk\too1s\monitor.bat.
Para remover uma aplicao pelo File Explorer basta selecionar o arquivo .aplc descjal
e clicar no icone com um trao - vermelho no canto direito superior da Jr
Observe que nessa janela tambm existem cones para enviar arquivos para O
emulador e para baixar os arquivos que esto no emulador para seu computad0r.
Note que toda a segurana do Android baseada na segurana do Linux. No
Android cada aplicao executada em um nico processo, com uma thread
dedicada. Para cada aplicao criado um usurio no sistema operacional para
ter acesso a sua estrutura de diretrios. Dessa forma nenhum outro usurio pode
ter acesso a essa aplicao. Por isso, se um dispositivo real estivesse conectado
na porta USB do seu computador, a janela File Explorer tambm funcionaria, mas
provavelmente no exibiria nenhuma aplicao por motivos de segurana. No
emulador como se o desenvolvedor tivesse acesso total (root) ao dispositivo.
O8''r'
lador; Inclusive j estudamos o comando adb shell para entrar na estrutura de
diretorios do Android.
mais d , , . 8 ~u. e
em segundo plano controla as portas de cada emulador
Para sair do prompt, digite exit. At aqui tudo foi simples, mas se existir mais de
um emulador aberto? Nesse caso o adb vai exibir uma mensagem de erro, pois ele
no sabe qual emulador deve acessar:
C:\android-studio\sdk\platform-tools>adb shell
error: more than one device and enulator
Esse comando listar os emuladores abertos, exibindo seu identificador, que con
tem a palavra emulator mais o nmero da porta em que ele est aberto, por exemplo,
emulator-5554. Saiba que a notao pode mudar conforme o emulador; portanto,
o importante voc entender o conceito. Para acessar um emulador especco,
podemos utilizar o comando adb shell novamente e informar o parmetro -s com
o id da instncia do emulador que desejamos.
C:\android-studio\sdk\platform-tools>adb -s emulator-5554 shell
O argumento -s pode ser utilizado para todos os comandos da ferramenta adb sem
pre que existir mais de um emulador aberto e for necessrio informar a instncia
correta. Por exemplo, digamos que voc deseje enviar um arquivo para o emulador.
Para isso, poder utilizar o comando adb push, conforme mostrado a seguir: _
63 1/tw
aa vv .
C:\androd-studio\sdk\p1atform-too1sadb s emuiator
0 KB/s (0 bytes in 7.000s)
Google Android - 4 @da
' t /data/1068
_ -5554 push c=\t@D\fQU1V-Ui
H outras opes de uso da ferramenta adb, mas vamos deixar isso como leitura
para voc:
http://developerandroid.com/tools/help/adb. html
Gostaria apenas de dar uma dica caso voc perceba que o Android Studio e o
emulador no esto conversando muito bem. s vezes, se o emulador ca mui
to tempo aberto e executamos uma aplicao no Android Studio, parece que a
aplicao no instalada no emulador. como se o emulador estivesse parado.
Se isso acontecer, pode ser necessrio matar o processo do adb.ee em execuo
no sistema operacional. Isso pode ser feito com o comando adb kill-server. E para
iniciar o processo do adb pode-se utilizar o comando adb start-server. Outra forma
de reiniciar o processo adb.exe abrir a janela Devices da ferramenta Android Device
Monitor e clicar no item de menu Reset adb.
iar ~
emulador do Android nos permite simular exatamente o tamanho e a resoluo
da tela de um dispositivo real. Para realizar essa configurao basta escolh
fz
Apenas para dar uma ideia, como cada aparelho tem uma configurao de tela,
podemos utilizar imagens de tamanho diferentes para cada tipo de resoluo.
Para isso no projeto Android dentro do Android Studio criada uma pasta de
imagem para cada resoluo. Por exemplo, existe a pasta drawable-ldpi para celulares
QVGA (24Ox32O) de baixa resoluo, a pasta drawable-mdpi para celulares HVGA
(32Ox48O) de mdia resoluo, a pasta drawable~hdpi para alta resoluo com as
telas WVGA (480x800) e por a vai. A coisa comeou a car to complicada que
chegou o xhdp (extra high) e a histria continua.
Essas pastas no tm relao com o tamanho de tela, por exemplo, 240x320 ou
(480x800), e sim com a densidade e resoluo da tela. O tamanho da tela pode
ser o mesmo, mas a quantidade de pixels que a tela consegue exibir pode ser di
ferente, e isso tem a ver com a resoluo. Portanto, aparelhos WVGA (480x800),
embora tenham telas maiores, tambm apresentam resolues muito melhores,
para exibir imagens e grficos com uma tima denio.
Google Android - 4 2d5
. ' .. ' xl
em fra_bida
~, , ~ . ~ )0 . ~ -' -tor inserida
- , . ' na pasta
drawablc-mdP~ .
M lima mmgtm dt M px 480). Mas o que acontece se ela for
hida
- _ ,em
Pcrleitaniente
ul uma
em
- . d` . re in
tela
uma tela
iensionada
H\/GAmenor ou maior.
fifa? 8 imagem
para baixo em uma_tela ..
Ser redimensiw
_ .. .' .asrado. or 15
na
la P
orPadrao.
s araCaso
umaatela
imagem
maior5<~J
como _ WVGA
ara evl '
QVGA (2~lO32O) no temos problem8S.fT121 P _ f P . SO
(~l80800) a iinagem val distorcer e Perder quhdade, O que E espc -t tr que 0
podemos customizar as imagens em suas respCflV5 Past* P
Android faa isso em tempo de execuo.
Neste exemplo, para imagem de 100px, taramos a seguinte conta:
Para telas QVGA (24O32O), seria 100px 0,75 = 75PX- N55@ (3350 P0del05
inserir uma imagem de 75px na pasta drawable-ldpi. Ou voce pode deixar
que o Android redimensione a imagem para baixo em tempo de execuao.
Para telas WVGA (480800), seria 100px 1,5 = 150px. Nesse caso podemos
inserir uma imagem de 15Opx na pasta drawable-hdpi, pois esses modernos
aparelhos conseguem exibir imagens com muito mais denio.
O nome da imagem ser sempre o mesmo. Por exemplo, se o nome do arquivo
for imagempng, teremos vrias imagens com o mesmo nome, inseridas nas suas
respectivas pastas. Em tempo de execuo, conforme a resoluo da tela, o Android
vai escolher a pasta correta, voc s precisa garantir que a imagem vai estar l. Um
exemplo prtico que temos disso o cone da aplicao, criado automaticamente
pelo wizard com o nome icjaunchenpng e replicado nas pastas mipmap-mdpi,
mipmap-hdpi, mipmap-xhdpi e mipmap-xxhdpi para customizar o tamanho para cada
resoluo. Isso evita que o Android faa esse redimensionamento em tempo de
execuo, pois os recursos j foram informados em tempo de compilao.
Vamos aprender mais sobre esses detalhes durante o livro, ento que tranquilo
Ou man . .
http://wwulivroandroid.com.br/
tenha contato nas redes sociais, que costumo postar sobre lan HITICII _
http://facebook.com/ricardolecheta Os
hUPS2//plus. google. com/+Ricardo Lehem
Captulo 2 I Congurao do ambiente de desenvolvimento 71
Caso queira apenas baixar o cdigo-fonte do livro, acesse o repositrio de cdigo
-fonte do GitHub nos seguintes endereos:
http://wwugithub.com/livroandroid/capitulos
http://wwmgithub.com/livroandroid/carros
http://wwwgithub.com/livroandroid/AndroidUtils
O repositrio /capitulos contm vrias pastas separadas por captulo. Aqui voc vai
encontrar os projetos prontos referentes aos exerccios de cada captulo. O reposi
trio /carros contm um passo a passo que vou utilizar durante o desenvolvimento
do projeto dos carros. Cada pasta corresponde a uma parte do projeto dos carros
concludo. O repositrio /AndroidUtils contm um projeto do tipo biblioteca com
classes utilitrias para nos auxiliar nos exemplos.
Pela explicao do livro, voc conseguir seguir os exemplos e entender o que
signica cada pasta. O objetivo dessas pastas servir de gabarito para voc.
Recomendo que voc tente fazer sozinho todos os exerccios de cada captulo e
o projeto dos carros passo a passo, mas se em algum momento tiver problemas
basta conferir o resultado com os exemplos do Gitl-Iub.
Para fazer o download do cdigo-fonte do GitHub voc pode simplesmente clicar
na opo Download Zip. Outra opo clonar o repositrio na sua mquina, assim se
eu fizer qualquer atualizao, como por exemplo uma possvel correo de bug
ou melhoria de algum exemplo, voc sempre pode atualizar o repositrio.
Com o Git instalado na mquina, abra um prompt e digite o seguinte comando
para clonar o repositrio:
git clone https://github. com/livroandroid/carros.git
Depois para atualizar o cdigo caso existam atualizaes utilize o comando gt pull.
Uma vez que voc fez os downloads dos exemplos, utilize o menu File > Open do
Android Studio para abrir o projeto desejado. Agora com voc, leia com calma
cada captulo e sempre conra as explicaes com os projetos de exemplo, que
podem servir de gabarito para voc.
Vamos l! Desejo desde j uma boa leitura.
\ cAPiruLo 3
Conceitos bsicos do Andr0d
-4
1
I
. , -- ~- ''m
criarrimir
telas na
Este captulo aborda alguns conceitos basicos do Android, como
aplicao, denir uma interface graca s1mpleS, fafaf eventos da tela e l P
mensagens (logs) da aplicao utilizando a ferramenta I.ogCat.
Um layout de tela no Android pode ser criado utilizando um arquivo XML que
dene os elementos da tela ou utilizando diretamente as classes da API Java. A0
nal deste captulo, voc ser capaz de criar telas simples na aplicao, demr
eventos para os botes e ainda visualizar os logs gerados pela aplicao.
72
Captulo 3 I Conceitos bsicos do Android 73
z_*^*"1*=i=
. fi . A ? ?i;*'"f W
1
*gif .gitignore
builcigradle
ll . 5
i;_igradle.prop:r1ies
'fg
E1-;3 l__,_z;
gradlew
gradiewbat
gii Iocai.properties
' settingmgradle
5 z ag; External Libraries
u
74
it ' ' 1
pszati '
HelloAn'li (\l('l('|lO `
. tr wi"
i
1.
v-.ii
.l
l
rrliz .L^
. *1]l lu'
i .il. _, . .i
s ul .l"
V ,W { _' lui, z
i\'..il~~
,, t, I,l\. .2
~\li.l\N'_{il' Kill
.lltiw lltzr
lxl l .
l
.till
,,),r17z.ll Nil);
ompzn ll) *WMV
www ii ll 'ii ^`
ii ' ii V,
3
gl
d bl t '
Pasta que contm os recursos da 'ipz l'reao. como imagens, layouts
de telas e arquivos de internacionalizao. Existem cinco subpastasz
WW f.l1y0ul. menu. mipmap e values, com os seus respectivos classi
cadores, que vamos vericar mais adiante.
Captulo 3 i Conceitos bsicos do Android 75
Pasta Descrio
res/drawable Pasta com as imagens da aplicao. Atualmente como existem diver
sos dispositivos Android com diferentes resolues de telas, possvel
customizar as imagens para car com o tamanho exato em cada reso
luo. Para isso, h diversas pastas para as imagens: drawable-ldpi (low),
drawable-mdpi (medium), drau/able-hdpi (high), drau/able-xdpi (extra high)
e drawable-xxdpi (extra extra high). Na ltima verso do Android Studio
ele no cria todas as variaes, eu criei para mostrar a voc.
res/mipmap Pasta com o cone da aplicao, o qual por padro chama-se ic_launchex png.
Da mesma forma que a pasta /res/drawable, esta pasta tambm apresenta
variaes conforme a densidade da tela do dispositivo.
res/layout Pasta que contm os arquivos XML de layouts para construir as telas
da aplicao.
res/menu Pasta que contm os arquivos XML que criam os menus da aplicao,
que so os botes com aes na action bar.
res/values Pasta que contm os arquivos XMLutilizados para a internacionalizao,
congurao de temas e outras conguraes.
Cada arquivo seja imagem ou XML dentro da pasta /app/res contm uma referncia
na classe R, que automaticamente gerada ao compilar o projeto. Cada vez que
se altera, adiciona ou remove um arquivo dessas pastas, a classe R alterada para
reetir as informaes.
Por exemplo, se voc inserir uma imagem chamada smilepng em uma das pastas
/res/drawable, a constante R.drawable.smile ser automaticamente gerada na classe R.
Se voc inserir um arquivo XML chamado teste.xml na pasta /res/layout, a constante
R.layout . teste ser gerada. A classe R nossa grande aliada no desenvolvimento para
Android e nunca deve ser alterada manualmente. Observe que o cdigo-fonte da
classe MainActivity utiliza a constante R.layout.activity_main, a qual define o layout
utilizado para apresentar a tela ao usurio.
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain); // O layout da tela denido aqui
}
@0verride
oooqio Android ~ 4 d|
76
publlt haolvan on(roato0ntlomMenu(Henu menu) J menu). // Menu com botes na action bar
9t*lMt~nulnalr().ln\tt*(R"''~"cUV{ty~m n' A '
return tlui
1
\ _` , .z . -. ~ (UIC\'l\`;ll'ClI\tflllll
. .~ '. . - I; -Li.: *MalnActlvlty.rt \ttH"" U V Q
NO!l.l\titnt||ti Itmlt t lx vt l g I. ` H h.. mm`mc|1I(* tl
kh) Jg,R_1yaut.actlvlty_maln. /\inIrnt . tua n
l ~ ~ ' ^ W.1|ml.Isszitt1iic;it1H1
nltquivn dc 'nx- H /,rx/l`?|)|((/|( H'_\'_ HUN". \", lu) UIIUI \ P*
ii Ctrl+cllque utilizntln cm niuittis mitrns |\|i24"`<`~
1% Andro|dMan|fest.mI
- d tl _ i
androld:theme="@5tyl@/Apprhemev , Tema d i It
octlvlty 00 Cl/0 (/tvs/values/styles.ml)
androld:name="_n1ntV
Y" // Classe da Actlvlty que deve ser a@u1;d,
"f*<'=11"'@Slfloo/ooo nan@'~ // Titulo
lntont.ltor
A // Declara um ltro para exect avals mostrar na action bar
// ao MAIN indica que tff BCVY D0de ser executada com
|ctlon ondrold:n|mo~"|ndrold.lntont.|ctlon.HAIN" / O a inicial
Captulo 3 I' Conceitos bsicos do Android 77
// A categoria LAUNCHER indica que o icone da activity deve car disponivel
// na tela inicial
<category android:nane="android.intent.category.LAUNCHER" /
</intent-lter>
O XML sempre deve iniciar com as linhas a seguir, declarando o namespace para vali
dar os atributos utilizados e o pacote da aplicao. O encode do XML deve ser UTP-8.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.livroandroid.helloandroidstudio">
tiviz Obrigatrio
Notazsempre . . . - t inicia a
que criarcongur-la
uma nova ac yO
_ _afqulvo
_ _ , ld
AndroidManifesz.xml. A Ma1nAct1v1ty foi configurada como a EICUVI Y_
LAUNCHER. Mais detalhes
aplicao, pois est declarada com a ao MAIN e a categoria 1 Intent
sobre 3 tag <intent-1ter> sero explicados no capitulo 20, sobre a c asse .U
A classe ManActvty ou qualquer outra activity deve ser lha da classe androd.
app.Actvty.
An d `
ar '
~._1 e
'
desc h d _na ac tvtty
n.A.nao
t .l ~sabe
voc criar uma classe-lha de androd.app.Actvty. O mtodo onCreate(bund1e) pre
_' app
old.
cisa ser implementado obrigatoriamente e chamado de forma automtica pelo
n roid quando a tela e criada No entanto a classe andr ' '
21 Ila e para isso precisa da ajuda da classe andro1d.v1ew.Vew, que
POY SUH VZ Se encarrega de desenhar os componentes visuais como cam d
fex, botoes e imagens. Para isso existem diversas subclasses especializafias de
mostra o na guravericar
33 No asdiaclas
~ ml
' Q
andro1d.1ew.View, conforme podemos verificar no diagrama de classes resu .d
_ grama podemos
Ed1tTet, Inagevew etc., responsveis por desenhar . I SCSITEXVIEW, Button,
os e ementos gracos da tela
Captulo 3 n Conceitos bsicos do Android 79
G andro|d.app.Activity
0 f|ndViewById(id: int): android.view.View _ G an ro Cl Id. ` .Vl
View ew
O setContentV|ew(layoutResID: int): void O dfBW(C6I'IV51 android-cr=DhicS.CvS)= VO
0 setContentView(view: android.view.\ew): void V
4 V l V A \
G android.widget.TetView G androidiwidgeumagewew Q and,.d_Vew_VewGmUp
Q android.widget.AbsoIuteLayout \\
G ndroid.widget.Button j G android.widget.EditTet | _ G android.widget.ReIativeLayout
Q andro|d.widget.FrameLayout
Q android.widget.L|nearLayout
G android.widget.TableLayout
Na classe Activity, podemos vericar pelo diagrama que existem dois mtodos
setContentView(view); o primeiro recebe um argumento do tipo View, e o segundo recebe
um nmero inteiro indicando o recurso de algum arquivo XML de layout. Isso porque
no Android possvel construir a tela de duas formas: diretamente pelo cdigo-fonte
Java, ou utilizando um arquivo XML de layout que define a view que ser exibida.
Independentemente de qual verso do mtodo setContentView(view) for chamado,
seja informando um objeto ou um arquivo de layout, esse mtodo responsvel
por fazer a ligao entre a activity e a view que ser responsvel por desenhar a
interface grca da tela, e deve sempre ser chamado durante a execuo do mtodo
onCreate(bund1e) da classe Activity.
Nota: para cada tela da aplicao existir uma activity para controlar seu estado e
eventos, mas para denir a interface grca da tela utilizada uma view. Dentro
da activity necessrio chamar o mtodo setContentView(view) para informar a
view responsvel por desenhar a interface da tela.
` f "
I ' tu;aI11Cl
_
automa .
Google Android - 4 dl
ns v' ~ '
cliarnado Ll*m*l *lltllm l><*f=' U cm dk lmllm JGIOQ estudar tudo isso dep0I5
enquanto no se l`\UP mm essa plrtc' I (
/res/layout/activity__main.xml
<?ml version="2.G" encoding="utf-8"?>
<RelativeLayout xmlns:androtd="http://schemas.androtd.com/apk/res/androtd"
xmlns:tools="http://schemas.android.com/tools"
androtd:layout_width="match_parent" // Largura da tela
android:layout_hetght="match_parent" // Altura da tola
androtd:paddtngLeft="@di.men/activtty_hortzontal_margin" // Espaamento na esquerda
androtd:paddngRtght="Qdtmen/acttvtty_hortzontal_margtn" // Espaamento na direita
android:paddtngTop="@dimen/activty_vertical_margtn" // Espaamento no topo
androtd:paddingBottom="@dtmen/acttvity_vertical_margln" // Espaamento em batxo
tools:contet=".HalnActtvity">
_ML
. _ un
.'~ ( k ` ai `
' ~' '- " Q af UWUS
_
No cdigo loi utilizada a sintaxe @d
tmen para acessar valores de espa im
;1m dehnidos no arquivo /res/values/dinicms.xml. Deixar essas constantes 1 qul fo
0 0 Cm
_ _e poder customizar
constanteso uand
valor d -l J. I
Zad I, 13 boa pratica porque podemos alterar o cdigo ein um lugar cekiltr' l`
essas
exemplo, para uulizarum valor dihei . _, , , _., q 0neCeS*m PW
tte para a versao tablet do mesmo aplicativo.
Captulo 3 1 Conceitos bsicos do Android 31
/res/values/dmens.xmI
Nota: para cada tag denida no arquivo XML de layout existe uma classe
correspondente que filha de android.view.View. Para exemplificar, as tags
<TetView>, e correspondem s classes android.widget.TetView,
android.widget.InageView e android.widget.Button.
muito no desenvolvimento.
-visualizao para diferentes tamanhos de telas. Esse recurso incrvel e ajuda
82 ..' .. ... ,_ ._ Goo9Ie Android - 4' d9
,,z.i
"o Q .~.z--~ - ooo e
.. . tz.. ii W o ~~~
izz,..,.... iai- ea tu
g 1 lfnmel YU' 7 .. .J
i l u l ineml you! (Homontd),
i [mari you! (VCWU i
Tablcl you!
W mzunow
l `5uyn
llljkclmvelayout
E Cl W'
Plam Tcxwitvv
i gm; urge Ten
i Wii Medium Text
E *Abi Small Text
J Button
rz ] p 4
wi Smallufkon
ll \Rdo8u!!on
~.
Q _ .`_...z.. - --fz
l__QCheclr8ox
i! (tuto
r iLmsupevo
I ~., m ifpw
tablet ou ate mesmo no Google TV
jmlquuh
3"'*Y-"""-W' '* vt A i _l `
G' ' "Nmn4~ - Owomm "Muvmmy- 6- vwzy. ii i
on bw __ Q'
i l *reohilcyoutduge tu
l UQIQOQNH..
l V *"*~vw *r;~~e~,i1.!-\r~.t.~
L - ii 'hlxilv zilug-z,
z F3 \ Pim.. nrguwim tm
hl pl Andmud Vamu-u
i'\*l li: r-= iva fz.1\i
' V-'Y-' la.z ;l\r rx
' ei .
i
'_ .Y vlw
lr ieo,'-Jp c
/res/values/strings.xmI
<?ml version="2.G" encoding="utf-8"?>
<string name="app_name">HelloAndroidStudio
<string name="hello_world">Hello world!
<string name="action_settings">Settings
O nome do aplicativo denido pela chave app_name, e o texto que aparece na tela
denido pela chave hello_world. No arquivo /res/layout/actii/ity_main.xml, ao denir
a tag <TetView>, foi utilizado o atributo android:tet="@string/hello_world" para refe
renciar a mensagem denida pela chave hello_world. O padro para acessar essas
mensagens, como voc j deve ter percebido, @string/nomeDaChave.
Para criar um aplicativo com suporte a diversos idiomas, basta criar uma pasta
/res/values/values(cdigo do idioma) e traduzir 0 arquivo /res/values/strings.xml . A seguir
temos um exemplo de uma pasta com as mensagens em ingls e outra em portugus.
/res/values-en/strings.xml
/res/values-pt/strings.xml
3.6 Classe R
R.java
public nal class R {
public static nal class attr { }
84
l} _i
public static nal class dcawable a 20000.
Dublic static nal int icon=97 0 f
naente!
Nota: a classe R nunca deve ser alterada manual -F __ _
A tag android:icon dene o cone do aplicativo que vai car na tela inicial do
Android, sendo que a sintaxe @drawable utilizada para acessar a imagem
/res/drawable/ic_launchexpng. Essa sintaxe padro para acessar imagens sempre
que voc estiver em algum arquivo XML. Por exemplo, ao adicionar uma view
do tipo Imageview no arquivo /res/layout/activity_main.xml, podemos mostrar uma
Porm cas
gura utilizando a notao @drawable/none_gura:
ImageView android:src="@drawable/none_da_gura"
_ _ - os e mr um
1 _ _ _
android:layout_width="wrap_content" android:layout_height="wrap content" />
magem mamicamente
o voc precise acessar esse Imageview e atualizar a i d'
pelo codigo Java, vai precisar utilizar a classe R Primeiro precisam d `
identicador para o Inageview com a tag android:id e a sintaxe @+id/identgado
<InageView android:id="@+id/img"
- ~ _ 9 = wrap_content" />
android:layout_width="wrap content" androidlay0ut hei ht "
Para acessar esse objeto Textview no cdigo, basta utilizar o mtodo ndViewById(id).
Note que a constante R.string.hello_world utilizada com objetivo de acessar a
mensagem hello_world que est denida no arquivo /res/values/stringsxml.
// Encontra o objeto pelo id
Textview text = (Textview) ndViewById(R.id.text);
text.setText(R.string.hello_world); // Atualiza o texto dinamicamente.
A fim de encerrar o assunto, vamos fazer uma breve reviso. Lembre-se de que a
notao com o caractere @ utilizada sempre que for necessrio acessar um recurso
em um arquivo XML. Caso o recurso precise ser acessado no cdigo Java, preciso
utilizar a classe R. A tabela 3.1 compara as duas formas de acessar os recursos:
buiId.gradIe
buildscript [
repositories {
jcenter() // Repositrio padro do Android
}
dependencies {
// Verso do plugin do Gradle. Se o plugin for atualizado atualize este cdigo
// Caso tenha um erro de compilao nessa linha, tecle Alt+Enter para abrir o assistente
// O assistente vai ajuda-lo a congurar a verso correta do plugin
classpath 'com.android.tools.build:gradle:1.0.9'
}
allprojects {
repositories {
jcenter() // Repositrio para todos os projetos
l
}
l
dependencies { // Declara as dependncias (bibliotecas) para compilar do projeto
// Inclui todos os arquivos .jar da pasta libs na compilao
compile leTree(dir: 'libs', include: ['*.jar'])
}
Nota: todo aplicativo (apk) deve ser assinado com um certicado digital antes
de ser instalado no dispositivo. Por padro o sistema de build do Gradle
compila o projeto no modo debug e release. O modo debug utilizado durante
o desenvolvimento, e o aplicativo assinado com o certicado digital de
desenvolvimento, o qual criado automaticamente pelo Android Studio e ca
na pasta do usurio ~/.android/debug.lceystore. O modo release utilizado para
publicar o aplicativo no Google Play e deve ser assinado com outro certicado,
o qual voc precisa criar. Vamos estudar mais detalhes sobre isso no captulo
38, sobre Gradle.
88 Google Android - 4 edio
3.9 Log(at - Escrevendo mensagens de log
Em java, para imprimir uma mensagem no console utilizado 0 coman
do Systen.out.println(string), mas no Android recomendado utilizar a classe
android.util.Log, que contm mtodos utilitrios para imprimir informaes com os
niveis de detalhes desejados, como informao (i), debug (d), warning (W) C ff0 ()~
Dica: no Android Studio, utilize a tecla de atalho Alt+6 para abrir a janela 6:Android.
Nessa janela voc ver os logs do LogCat,`que tem por nalidade gerenciar todos
os logs do sistema operacional.
MainActivity.java
inport android.util.Log;
public class MainActivity extends Activity {
private static nal String TAG = "livro";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
OOI
Observe que a constante TAG denida na classe utilizada como identicador das
mensagens. Isso til para ltrar as mensagens por tags/categorias, facilitando a
visualizao e debug. Os mtodos da classe Log so bem simples, e comeam com
a primeira letra do nvel de severidade. Por exemplo, para imprimir um log com
nivel de alerta utilizado o mtodo Log.w(tag,nensagen). O W da palavra warning
Captulo 3 n Conceitos bsicos do Android 39
(alerta). importante entender que necessrio informar a tag do log, para que
depois seja possvel ltrar os logs apenas da tag desejada.
Agora execute o cdigo e visualize os logs gerados na janela LogCat (Figura 3.6).
Observe que mensagens vermelhas no LogCat so erros. Neste exemplo lancei uma
. . logar ~-o'
exceo no cdigo para voc visualizar um erro de teste, portanto acostume-se a olhar
o LogCat, pois qualquer exceo lanada pelo seu aplicativo ser detalhada aqui.
os-oe 15:41 O4 son 1211-1217/2 I/Jdwpi rqnonnq se o dzbuqqez puizzq and droppinq 'A Y
h9~06 l5:4 04 500 1073-1079/7 I/Jdwpi Iqnoxring second debuqger ~- pcing and drcppinq j
59-96 5 4 O-1 500 1280-1286/? I/Jdwp= Iqnoring second debuqqer -~ a ceptinq and droppinq
O9 O6 15 Of; 680 402-416/? I/Choreoqrapheri Skipped 30 frame! Th pplication my be doing wo much work on its main t 1. 5
9 06 l5 4 OS 80 402-416/7 I/Chareographer Skipped 30 frames! Th pplmation may be doing too much work on its mam t `
9 O6 l i O5 490 1750-1750/? V/livro log de verbose
9 E 15 -i O5 3flO 402-1G/7 I/Choreographerf Skipped 36 xames! Th applic :on may be doing too much work cm 'cs main 1 `.' 1
9 3 15 05 4390 1750-1750'? Dlivre log de debug
O 15 ~ 05 4 O 1750-1150/7 I/livro' log de info
9 E 5 4 5 500 1 50-1750/? H/livro~ log de alerta
l ~l Sl 175 -1750/? E/livro log de erro
g R nti.:eE p 1 n neste de erro
i::.l1vroa ci d livroend:o1.d_cp03.Hair\r:t1..'i;y.onC:e'ce(Ma1rzAct:;v1tz.zava:31)
ac ar:1r:1_d.app A 1:ivir.y.performC:ear:e{ .`z ' ,_;ff;_*',, ~1)
andrf.:1d.a p I umencation.calLz'c:1vii:yCnCreate(` .~V ~ '''= ;.''";_V,.i.:f3; f)
c ndroid pp A t' ` _1'hrea .performLaunchActiv1t:y(%ir* i-:':.2, '1z~)
:X cl pp A \:` `1:y'1'h ea .hondleLaunchl1;ivity(.fz; ;.. iQ:j_;_-;;fff_'_f_)
d d pp A ` y'l'b .acces:r$E0O(`. m jzff ` f f f)
d d pp A y'Ih i.hand1e2$es:sege(..;:* . ; ' ~. _ ;; _)
+ - =~= Ml
Filter ogcat messages by different parameters.
Empty elds will match ali messages.
=LI=z=rff== W l
. by Log Message (regex):
by Pgckage Name: J
bz-flD= l_w__w_W_,_,__-_;
by Log Lad: VEIKE _
l-<=*l
Depois de criar o ltro, possvel visualizar somente os logs gerados pelo nosso
aplicativo (Figura 3.8). Agora podemos ver somente as mensagens que nos interessam.
Q- _
ioglfvel Verbos: lf '``- A`
i 41 1
1 il1 "
ql
'J
F
;) il J
J
l"
Pi
1 l
' l
J
l
Neste prximo exemplo vamos alterar o layout da MainActivity para criar uma tela
de login com dois campos para o usurio e senha, a m de fazer a validao desse
login de forma simulada. Neste exemplo vamos aprender no s a tratar o evento
de clique em um boto, como tambm a ler os valores digitados pelo usurio.
Para comear a brincadeira, altere o cdigo-fonte do arquivo /res/layout/
activity_main.xml conforme demonstrado a seguir:
Captulo 3 I Conceitos bsicos do Android 91
/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout nlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:gravity="center_vertical" android:orientation="vertical"
android:padding="16dp">
<TetView
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:tet="@string/usuario" />
<EditTet android:id="@+id/tLogin"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:inputType="tet" android:singleLine="true" />
<TextView
android:layout_width="natch_parent" android:layout_height="wrap_content
android:layout_narginTop="10dp" android:tet="@string/senha" />
<EditTet android:id="@+id/tSenha"
android:layout_width="match_parent" android:layout_height="wrap_content
android:inputType="textPassword" android:singleLine="true" />
<Button android:id="@+id/btLogin"
android:layout_width="2G0dp" android:layout_height="wrap_content"
android:layout_narginTop="6dp" android:tet="@string/login"
android:textColor="#ffffff" android:layout_gravity="center"/>
MainActivity.java
@0verride l
btLogin.setOnClickListener(new View.0nClickListener() {
});
}
Depois dessa alterao, execute o projeto para conferir o resultado (Figura 39).
Ao digitar o usurio "ricardo" e senha " l2.3", o login de teste realizado.
Captulo 3 I Conceitos bsicos do Android 93
Senha
..i
Desenvolvedores com alguma experincia devem ter percebido que o tratamento de even
tos no Android similar a qualquer outra linguagem. Na verdade, existem muitas formas
de escrever esse mesmo cdigo, basta informar ao mtodo set0nClickListener(listener)
algum objeto que implementa a interface and roid .view . View . 0nClickListener.
MainActivity.java
@0verride
public void onClick(View view) { // A prpria classe implementa View.0nClickListener
// Cdigo que trata o evento aqui
}
}
94 Google Android - 4 edio
A desvantagem dessa implementao que, se voc tiver mais de um boto na
tela, o mesmo metodo onClick(view) ser chamado para todos OS botes- NCSSC
caso o parmetro View indica qual componente gerou o evento, e deve ser utilizado
para descobrir em qual boto foi feito o clique, conforme demonstrado a seguir.
ii MainActivity.java
@0verride
public void onClick(View view) {
if(view.getId() == R.id.botao0k) {
// clicou no boto 1
} else if(view.getId() == R.id.botao0k2) {
// clicou no boto 2
}
Vejamos outra forma de escrever o mesmo cdigo, que criar um mtodo para
cada boto. Eu particularmente gosto desse jeito. A vantagem que fica simples
de separar cada clique em um mtodo diferente.
MainActivity.java
};
}
4.1 Activity
Uma activity uma classe que deve herdar da classe android.app.Activty ou de
alguma subclasse desta, a qual representa uma tela da aplicao e responsvel
por tratar os eventos gerados nessa tela.
A classe da activity deve sobrescrever o mtodo onCreate(bundi.e). Esse mtodo
obrigatrio e responsvel por realizar a inicializao necessria para executar a
aplicao, como por exemplo chamar o mtodo setContentView(view) para denir
a interface de usurio.
96
Captulo 4 n Activity 97
o pacote do projeto br.com.1vroandrod.cap4 e a classe tambm est nesse pacote,
podemos utilizar a sintaxe do ponto.
<activty android:name=".MinhaClasseActvty" />
No entanto, se a classe da activity estiver em outro pacote, como por exemplo br.com.
1vroandrod.cap4.activity, podemos declarar essa activity com a seguinte sintaxe:
<activty androd:name=".activity.MinhaC1asseActvty" />
Como exerccio, voc pode criar outro projeto no Android Studio e escolher a
API Level 9 (Android 23) como a mnima do projeto. Ao fazer isso, o Android
Studio vai criar 0 projeto utilizando a biblioteca de compatibilidade v7, e nesse
caso a ManActvity ser lha de AppCompatActvty. Bem, teoricamente era isso que
deveria acontecer, porm a verso do Android Studio que utilizo ao escrever este
livro gerava o cdigo antigo.
public class ManActvty extends androd.support.v7.app.ActlonBarActvty {
O correto seria usar a classe AppCompatActivty:
Nota: cada aplicao nativa do Android, como o browser, a tela inicial (Home),
a agenda e a prpria tela para discar para um nmero de telefone denida
por uma Activity. Portanto, esse conceito de pilha de atividades utilizado para
todas as aplicaes, sejam as desenvolvidas por voc ou uma aplicao nativa,
pois tudo funciona sobre a mesma arquitetura.
Captulo 4 I Activity 101
Agora partiremos para a prtica. Existem mtodos da classe Activity que podem ser utili
zados para controlar o estado da aplicao. Dentre eles j vimos o mtodo onCreate(bund1e)
que responsvel por inicializar a activity e dar incio ao seu ciclo de vida. Agora vamos
estudar outros mtodos importantes: onCreate(bund1e), onStart(), onRestart(), onResume(),
onPause(), onStop() e onDestroy().A gura 4.1 demonstra o ciclo de vida completo de uma
activity exibindo os possveis estados e a chamada de cada mtodo.
Fonte:
http://developer android.com/training/basics/activitylifecycle/starting.html
Resumzed H _
=.._\\ j (vision) |
onFiesume() onPause()
onResume0 \
____,,,; ':' O inli O Started O .z/O Paused
onStart()
<..\_ (vtibiei _`l (prauy
ontartii onStop() L
visible) 1
Mtodo Descrio
mtodo do ciclo de vida da activity
DebugActivity.java
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i(TAG, getC1assNane() + ".onSaveInstanceState() chanado.");
}
Essa classe imprime um log quando cada mtodo do ciclo de vida for chamado.
O log criado com a tag livro, portanto necessrio criar um ltro para essa tag
na janela do LogCat, conforme explicado no captulo 3. O prximo passo alterar
104 Google Android - 4 edio
a classe MainActvty para ser lha de DebugActivty, assim ela vai herdar todos os
mtodos que foram customizados na sua classe me.
i MainActivity.java
Agora clique no boto voltar (back) do emulador para sair da activity Isso pode
ser feito tambm pressionando a tecla Est. Observe que agora os mtodos onPause(),
onStop() e onDestroy() foram chamados para encerrar o ciclo de vida da activity
INFO/ID(8494): MainActivity.onPause() chamado.
INFO/ID(8494): MainActivity.onStop() chamado.
INFO/ID(8494): HanActivity.onDestroy() chamado.
Nesse caso, como o boto voltar foi pressionado, o sistema operacional sabe que
a activity precisa ser destruda. Por isso, o mtodo onDestroy() foi chamado, elimi
nando completamente a activity da memria.
Agora volte na tela inicial do emulador e entre novamente na aplicao. Isso vai
chamar os mtodos onCreate(), onStart() e onResume(). Ento, clique no boto Home
para voltar tela inicial. Observe que nos logs os mtodos onPause() e onStop()
foram chamados, mas no o mtodo onDestroy().
INFO/ID(8670): ManActvity.onPause() chamado.
INFO/ID(8670): ManActivty.onStop() chamado.
Agora pressione a tecla de atalho (trI+F11 do emulador para girar a tela para a
l1()l'l2t)Ill`ill.()S logs a segttir mostrain que a activity foi destruda e recriada, mas
durante esse processo o metodo onSaveInstanceState(bundle) foi chamado.
Ma'tnActlvtty.onPause() chamado.
MatnActtvtty.onSaveInstanceState() chamado. // Salve o estado aqui
HalnActtvlty.onSt0D() chamado.
MatnActivity.onDestroy() chamado. // Actlvty foi destruda
MalnActivlty.onCreate() chamado: Bundle[{...}]}] // Actlvlty foi recrlada. Recupere o
// estado aqui
MainActivlty.onStart() chamado.
MatnActivtty.onResume() chamado. // Acttvtty est visivel para o usurio
Se voc salvou valores no Bundle l no mtodo onSaveInstanceState(bundle) possvel
recuperar este estado no Bundle que voc recebe como parmetro no mtodo
onCreate(bundle). para isso que serve esse Bundle no mtodo onCreate(bundle). Se
for a primeira vez que a activity executada, esse Bundle sempre estar nulo.
Entretanto, no caso da rotao da tela em que o Android faz esse processo para
recriar a activity, o Bundle pode estar preenchido cont os dados salvos no mtodo
onSaveInstanceState(bundle)_
Essa teoria inicial para voc j ficar esperto, mas no captulo 8. sobre fragments.
vamos revisar esse conceito com exerccios praticos.
.~ Cuz
ff; _ . XCtri+
l Rle .C Java Class
/res/layout/actvity_bem_vindo.xmI
<?xml version="1.G" encoding="utf-8"?>
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
mlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:padding="10dp" >
TextView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
classe.xml. _
Nota: o arquivo de layout XML segue a nomenclatura /res/layout/activity_nome_
<application ...>
<activity android:nane=".MainActivity" .. >
. . . // intent-lter da MAIN aqui.
O prximo passo alterar o cdigo da classe MainActivity para navegar para a activity
BenVindoActivity. Na classe MainActivity zemos o exemplo do layout de login e vamos
continuar de onde paramos. Caso o login seja feito com sucesso, vamos navegar para
a prxima tela, a qual vai mostrar uma mensagem de bem-vindo ao usurio. Nesse
exemplo vou passar um parmetro nome=Ricardo xo para a prxima tela.
MainActvty.java
};
}
Por isso, geralmente comum ver no cdigo-fonte o contexto sendo referenciado com
o this da classe, pois o this no Java representa a instncia do objeto atual. No caso de
o cdigo estar dentro de uma classe Activity do Android, o this representa essa activity
Intent it = new Intent(this, BemVindoActivity.c1ass);
startActivity(it);
Eu muitas vezes gosto de declarar a varivel context conforme demonstrado a
seguir. Neste exemplo simples no tem muita utilidade, mas em exemplos mais
complexos vale a pena.
nal Context context = this;
Intent it = new Intent(contet, BemVindoActivity.c1ass);
startActivity(it); '
Veja que no cdigo do mtodo onC1ick(view) da MainActivity no foi utilizado o
this para passar o contexto. Isso porque o mtodo onC1ick(view) criou uma classe
interna (inner class do java), e nesse caso o this faz referncia classe interna c
no classe MainActivity. Por isso criei o mtodo96tC0f@'C() para retornar o this
e facilitar o cdigo.
111
Captulo 4 n Activity
1g Login
` V CapD4-Activity
i . v ' I..i ::/ '
li
i L'._..:r~'..= Ricardo Le|;li;=u, aaja bcrri vinda
i ricardo
ii
1 :`~._1.':i
Agora vamos voltar a falar do ciclo de vida da activity Ao abrir a segunda activity
ela inserida no topo da pilha de atividades e a tela do login est parada em
segundo plano. Para conrmar a teoria, vamos verificar os logs na janela LogCat.
Ao abrir a primeira activity podemos vericar nos logs que os mtodos onCreate(),
onStart() e onResume() foram chamados normalmente como esperado.
INFO/ID(11033): MainActivity.onCreate() chamado.
INFO/ID(11033): MainActivity.onStart() chamado.
INFO/ID(11033): MainActivity.onResume() chamado.
Podemos vericar que a activity MainActivity teve seus mtodos onPause() e onStop()
chamados para deixar a primeira tela em segundo plano. j a activity BemVindoActivity
teve os seus mtodos de inicializao chamados e agora ocupa o topo da pilha.
112 Google Android - 4 edio
Entretanto, o que acontece se 0 boto voltar for pressionado? O sistema operacional
identificar que a activity BemVindoActivity deve ser destruda, porque teoricamente
ela no mais necessria. Isso far com que a activity MainActivity seja relniciada,
voltando ao topo da pilha. Esse fluxo pode ser vericado pelos seguintes logs:
INFO/ID(11033]: BemVindoActivity.onPause() chamado. // A segunda activity Dfd
INFO/ID(11G33] HainActivity.onRestart() chamado. // Reinicia a primeira activity
INFO/ID(11G33 HainActivity.onStart() chamado.
INFO/ID(11033 HainActivity.onResume() chamado. // Volta ao topo da pilha
INFO/ID(11033 BemVindoActivity onStop() chamado.
INFO/ID(11033] BemVindoActivity.onDestroy() chamado.// Destri a segunda activity
Observe que os logs demonstram que a activity BemVindoActivity foi destruda, uma
vez que a chamada ao mtodo onDestroy() foi realizada. j a activity MainActivity foi
reiniciada, chamando os mtodos onRestart(), onStart() e onResume(). importante
que voc exercite o conceito de ciclo de vida de uma activity brincando bastante
com os logs do emulador.
Lembre-se de que os eventos extemos, como o usurio atender uma ligao telefnica,
tambm podem parar a activity e deix-la executando em segundo plano. Os mtodos
do cielo de vida devem estar preparados caso isso aconteaj que o exemplo da ligao
foi lembrado, vamos aproveitar e finalizar o assunto com chave de ouro e simular uma
chamada telefnica, para que a activity que est executando seja inserida em segundo
plano enquanto o usurio atende a ligao. Caber a voc executar a activity analisar
os logs e estudar o que ocorre quando o usurio atende uma chamada.
Para simular a chamada telefnica, abra a ferramenta Android Device Monitor no menu
Tools >Android >Android Device Monitor e procure pela janela Emulator Control. Nessa janela digite
3 .es Qi Z ._
um nmero de telefone no campo incoming Number e clique no boto (all (Figura 4.4).
L "`. Y.
Lembre-se: se alguma activity estiver em execuo enquanto a ligao for feita, ela
ser removida do topo da pilha e car parada em segundo plano. Agora quem
ocupa o topo da pilha a prpria aplicao nativa da ligao. Como assim, a pr
pria aplicao da ligao? Isso mesmo, a tela que exibe a ligao, assim como a tela
inicial, o browser, os mapas, a agenda de contatos etc., todas elas so uma activity
s que nativas e j disponveis no Android. Aos poucos voc vai entendendo que
no Android tudo funciona da mesma forma, seja uma aplicao desenvolvida por
voc, seja uma nativa do Android.
Voltando ligao, quando ela terminar e o usurio fechar a tela, a activity que
estava parada em segundo plano ser reiniciada e voltar ao topo da pilha, estando
pronta para interagir com o usurio novamente. Esse comportamento incrvel, j
que o sistema operacional encarrega-se de tudo. Ele coloca a activity em segundo
plano e depois a reinicia quando necessrio, e, claro, todos os mtodos de ciclo
de vida so chamados para o seu controle.
Depois de toda essa teoria no se esquea de brincar um pouco com o emulador:
e muito importante entender o funcionamento do ciclo de vida da classe Activity
114 Google Android - 4' edio
Nota: se existirem muitos processos parados em segundo plano e se as condies
de memria estiverem baixas, o sistema operacional pode decidir encerrar o
processo de uma activity, chamando o mtodo onDestroy(). O fato que 0 sistema
operacional encarrega-se do ciclo de vida da activity. Cabe ao desenv0lvCClOT
apenas implementar os mtodos desse ciclo corretamente.
Nesse exemplo uma string foi adicionada como parmetro, mas a classe Bundle tem
mtodos para diferentes tipos primitivos, como por exemplo: putBoolean, putChar,
putByteArray, putShort, putInt, putlong, putFl_oat, putDouble e vrios outros.
Se quiser voc pode escrever esse cdigo de maneira mais simplificada, pois a classe
Intent tem alguns atalhos para passar parmetros. Nesse caso podemos substituir
o trecho de cdigo anterior por apenas trs linhas. Internamente a classe Intent
vai continuar utilizando 0 Bundle, mas isso ca encapsulado.
public class HanActvity extends DebugActvty {
4.7 O bsico sobre action bar e como voltar para tela anterior
No ltimo exemplo, mostramos como navegar entre duas activities da aplicao.
Sempre que uma activity chamada, surge a necessidade de voltar tela anterior,
e para isso voc pode utilizar o boto de voltar do Android, ou colocar o indicador
de voltar na action bar.
Para adicionar o indicador de voltar na action bar, basta utilizar o mtodo
getActionBar().setDisplayHomeAsUpEnabled(true), e quando o usurio clicar no boto ele
vai disparar a ao de menu com o identicador android.R.id . home. Para testar a brin
cadeira, altere o cdigo da classe BemVindoActivity conforme demonstrado a seguir:
BemVindoActivity.java
@0verride
public boolean on0ptionsItenSe1ected(MenuItem item) {
int id = iten.getItemId();
if(d == android.R.id.hone) {
// 0 mtodo nsh() vai encerrar essa activity
nsh();
return true;
}
return super.onOptionsItemSe1ected(tem);
}
Desta vez, ao executar o projeto voc ver que na action bar da segunda activity
apareceu a seta que aponta para esquerda (Figura 4.6), indicando que o usurio
pode voltar para a tela anterior. O indicador de voltar na action bar chamado
de up navigation e vai adicionar uma pequena seta para esquerda indicando
que o usurio pode clicar nela para subir na hierarquia de telas.
O evento gerado pelo up navigation como se o usurio tivesse clicado em qual
quer outro boto da action bar. Nesse caso, porm, o identificador do evento a
constante androd.R.d.hone da classe androd.R nativa do Android. Observe que no
cdigo, como temos de voltar manualmente para a primeira activity chamado o
mtodo nsh(), que faz com que a activity atual seja encerrada. Assim, o Android
vai remover a activity da pilha e chamar todos os mtodos do ciclo de vida como
onPause(), onStop(), at o onDestroy().
Senha
` lllliiit
,__..___ _ ___-,_mJ
Nota: acostume-se com o termo up navigation, o qual se refere seta de voltar que
colocada na parte superior esquerda da action bar. Outro detalhe importante:
para encerrar uma activity por programao utilize o mtodo finsh(). Ao fazer isso,
o mtodo onDestroy() da activity ser chamado para encerrar o seu ciclo de vida.
'i "A
usurios de Android j esto acostumados com ela, pois todos os aplicativos
nativos so feitos assim.
Para entender a action bar, vamos estudar a figura 5.1.
1. App Icon
Por padro o cone da action bar mostra o cone do projeto e pode ser
customizado con forme sua necessidade. Nesse espao tambm mostrado
o up navigation que a seta para esquerda que indica que o usuario
pode navegar para cima na hierarquia de telas.
118
Captulo 5 I Action Bar e temas 119
Esse cone atualmente me deixou um pouco intrigado, e me fez pensar
um pouco antes de escrever as prximas frases. Desde que a action bar
foi criada, a documentao ocial refora o conceito de que esse cone
representa algo importante, como o logo da aplicao. Porm, com o sur
gimento do Material Design, a action bar nos dispositivos com Android 5.0
ou superior no mostra o cone, diferentemente de como era no Android
3.x e 4.x. No Material Design dado um foco maior na cor principal da
aplicao (primary color) versus a cor de acentuao (accent color) para
dar destaque s views e a alguns componentes. No Material Design uma
boa prtica customizar as cores da action bar para se identificar com a
marca do cliente ou aplicativo. Tambm recomendado utilizar a cor de
acentuao para destacar as principais aes quando necessrio.
Por isso, dependendo da verso do Android, voc ver aplicativos que
mostram o cone na action bar ou no.
2. View control
3. Action buttons
4. Action overow
O segredo para utilizar a action bar est no tema que o aplicativo utiliza, o qual
configurado no arquivo AndroidManiest.xml.
A interface do Android funciona com base nos temas. Desde sua criao os dois
temas clssicos eram o Theme.B1ack e Theme.Lght. O tema Theme.B1ack utiliza o fundo
preto e fonte branca e o tema Theme.Lght o contrrio, utiliza o fundo branco e
a fonte preta.
120 Google Android - 4' edio
lisscs eram os temas ate o Android 2.x, mas a partir do Android 3.0 (Honeycmb)
foi criado o tema Holographic, popularmente conhecido apenas como Holo. O tema
Holo tambm tem suas variaes com fundo preto ou branco, que so os temas
Theme.Holo e Theme.Holo. Light.
._ 'E'
L~ z~;;\ 's
., -:z1.L.'
*ef ~i..'
.' \|.z,.'
_ ge 'ef'
JU
z ha..
f .>=.:~.f.i
.: ,.,:_>
, a, rar; ~
*`.1_zrc':
s;c e
''7 ..rr;.zE' `
Dl* 'fz'
Por enquanto vamos deixar para l este AppTheme, depois voltamos a falar dele. Agora
vamos brincar um pouco com o editor. Para trocar o tema que o editor utiliza, clique
no combo do AppTheme e escolha um dos temas Classic > Black ou Classic Light Light.
Esses so os temas utilizados at o Android 2.x (Figura 53). Observe que nesse tema
no existe a action bar, pois antigamente na barra superior era mostrado apenas
um pequeno ttulo, que inclusive muitas aplicaes deixavam oculto.
.
Captulo 5 I Action Bar e temas 121
I
f* MW-mfm' * <ff~*f.f~==~I .aa, .r
2E C:zll@~u=~
J,i \..
-\*fa*ul-.`
l Cap-Activity
. senna
l
Li, ___ __
I
QQ2El_,-
BI=
2 1;
' '
* 'im' s'_
5 W
*afif,
. .-~-
i ll-~.,
:al .~~
l -.I
ll H:p_.~_
wuu-
Stnha
fzi i at ,Q5
Gium - 9- ''21'
Com a chegada do Android 3.0 (API Level 11) e os temas Theme.Ho1o e Theme.Ho1o. Light
a histria mudou, e nasceu a action bar, que representa o padro de design mais
.
importante do Android, pois nela que cam os botes com as principais aes
do aplicativo. Para fazer a pr-visualizao do layout nos temas Holo e HoIo.Lght
basta selecionar a verso do tema desejado no editor (Figura 5.4).
I M . _.
T activity__main.m1 X . _. . A activity maimxml > li V A
I32- =*
tvi, ,=
#25 ,Wu
lg ig; uzuz4~ ' Qoie "H @~ vzi- lg Q; g;Nzus4v - lwqm W- h rivai
El
Na pr-visualizao voc vai perceber que o tema Holo padro tem fundo preto
e a fonte tem uma cor azul parecida com a do filme Tron: O Legado. j o tema
Ho1o.Lght tem o fundo branco com fonte preta, e a action bar fica com um fundo
cinza. importante entender que no tema Holo (escuro) os botes da action bar
devem ser brancos. E no terna Ho1o.Lght (claro) os botes da action bar devem ser
escuros, assim a visualizao dos botes da action bar cam coerentes. Cada tema
tem diversas variaes; no entanto, isso voc vai perceber com o tempo. Uma das
,ii1; 1lI.
122 Google Android - 4 edio
\';1ria(es de tema mais utilizadas o Hoio.Light.DarkActionBar, qu d1X21 0 fUnd0
da tela claro igual o Ho1o.Light, mas o fundo da action bar ca escuro, Pfm1fmd0
1 . ._
utilizar botes brancos na action bar.
irI=~
uI | ut ___
Por ltimo voc pode selecionar no editor para visualizar o tema Material Dark e
Material Light, conforme a figura 5.5. Veja que a diferena entre o tema Material
para o Holo e que 0 I-Iolo mostrou 0 cone na action bar.
l , (N`]_fT1I.IYl K 1 0 dmty_man.xml - _
'yflll
'- ,. .il* ';- zz r;
if ' :,& fi . . , ~'' 5'
1 ' Ntxux 4' rs' Q'Mterra| W' ii: A 1- ' Nc\us4- ' ' ' fhf f112*'
* l3&&ii:'ic:.
` Gnplclviy i
E
l
l
I
L...._a.
l
i l
i
r
Certo, muito bem. Agora vamos voltar a falar daquele AppTheme que vimos no editor
visual. De onde esse nome surgiu? O tema AppTheme denido no arquivo /res/vaIucs/
styles.xml, e 0 editor consegue ler essa configurao e mostra-la para voc. Esse
o tema da aplicao, portanto, nele que vamos fazer as customizaes de cores.
A partir deste momento preste muita ateno, pois estou explicando com base
nos arquivos que foram gerados no wizard na poca em que este livro estava
sendo escrito. Mas 0 wizard frequentemente muda a forma de gerar os arquivos,
portanto o que importa voc entender 0 conceito. A seguir temos 0 arquivo
styles.xml, que foi criado pelo wizard, nele que 0 tema AppTheme foi definido. Esse
arquivo foi congurado para utilizar ou tema Holo. Veja que 0 tema AppTheme herda
de Theme.Ho1o.Light.
/res/values/styIes.xmI
AndroidManifest.xmI
<?xmI version="1.0" encoding="utf-8"?>
/res/values-v21/styIes.xmI
No Android, sempre que voc ver uma pasta com um trao e um nmero, como
por exemplo /res/values-1/21, porque esse nmero referente ao identificador da
API Level com o qual essa pasta compatvel. Nesse caso, o Android vai utilizar
por padro o arquivo /res/values/styles.xml, e se o dispositivo tiver o Android 5.0 ou
superior (API Level 21), a congurao feita no arquivo /res/values-v21/styles.xml
utilizada.
Settings
/res/menu/menu_man.xmI
<menu xmlns:android="http://schemas.android.com/apk/res/androd"
m1ns:too1s="http://schemas.android.com/tools" >
<item androd:d="@+d/action_search"
android:icon="@drawable/c_action_search" androd:title:"@string/action_search"
androd:showAsAction="a1ways" />
<tem androd:id="@+d/action_refresh"
androd:icon="@drawable/ic_acton_refresh" android:title:"@string/acton_refresh"
androd:showAsAction="a1ways" />
<item android:id="@+d/acton_settngs"
android:tit1e="@string/action_settngs"
androd:showAsActon="never" />
/res/values/strings.xmI
<?m1 version="1.0" encoding="utf-8"?>
Para utilizar esse arquivo XML de menu, a activity deve implementar o metodo
onCreateOptonsMenu(menu) e inar o menu XML. Isso simples conforme mostra
este cdigo:
@Override
public boolean onCreate0ptonsMenu(Menu menu) {
// Ina o menu com os botes da action bar
126 Google Android - 4' edio
getHenuInater().inate(R.menu.menu_main, menu);
return true;
Uma vez que o menu com os botoes da action bar esto configurados, basta imple
mentar o mtodo on0ptionsItemSelected(MenuItem) para tratar os eventos gerados pelos
botoes. Por isso no arquivo XML dc menu foi denido um id para cada ttcm. O
codigo-fonte completo da classe HainActivity do projeto pode ser visualizado a scgutr.
l: ManActivity.java
@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
getHenuInater().inate(R.menu.menu_main, menu);
return true;
}
@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_search) {
toast("Clicou no Search!");
return true;
} else if (id == R.id.action_refresh) {
toast("Clicou no Refresh!");
return true;
} else if (id == R.id.action_settings) {
toast("Clicou no Settings!");
return true;
}
return super.on0ptionsItemSelected(item);
}
}
Capitulo S I Action Bar e temas 127
Esse cdigo est mostrando um alerta ao clicar em algum boto da action bar,
portanto execute o projeto no emulador e congura o resultado. Conforme po
demos verificar, adicionar os botes na action bar realmente simples.
Indica que o boto sempre deve car visvel como action button. re
comendado utilizar essa opo para denir as aes mais comuns do
aplicativo.
w1thText
co11apseActonVew
Esse atributo indica que uma view que geralmente grande deve ser
contrada para exibir apenas o boto. Esse 0 caso do boto de busca do
Android, que ca contrado, mas ao ser clicado se expande para o usurio
digitar o texto.
Com o tempo voc entender melhor cada um desses itens, mas lembre-se de
que geralmente recomendado utilizar as opes fRoom para garantir que se no
houver espao o boto aparea no menu action overow, e a opo never se voc
tiver certeza que a opo deve car no menu action overflovv Tambm possvel
combinar essas opes com o separador, como por exemplo: app:showAsActon="if
Room|wthTet". Agora com voc, brinque um pouco com essas opes e veja os
resultados no emulador.
1
Nota: segundo as boas prticas do Android, os action buttons (botes com
aes) devem ser utilizados para as aes mais comuns da tela, ou seja, as aes
que sero mais utilizadas pelo usurio. j as aes que so menos utilizadas,
como por exemplo Ajuda e Configuraes, devem car no menu action overflow
(menu flutuante).
/res/values-v21/styIes.xmI
<?m1 verson="1.0" encodng="utf-8"?>
Voc deve estar se perguntando de onde eu tirei essas guras. Felizmente existe
uma coleo com o template de vrios cones com as aes mais comuns, como:
atualizar, buscar, adicionar, deletar, copiar, colar, compartilhar etc. Para baixar o
pacote de cones da action bar, acesse a pgina da documentao da action bar e
procure pelo link Download the Action Bar Icon Pack.
MainActivity.java
import android.app.Activity;
import android.app.ActionBar;
public class MainActivity extends Activity {`
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_main);
ActionBar actionBar = getActionBar();
actionBar.setTit1e("Captu1o S");
}
Ao executar esse exemplo no emulador, voc ver que o ttulo da action bar ser
Captulo 5' Existem vrios mtodos na classe android.app.ActionBar e a lista a
seguir contm alguns dos mais importantes.
setCustomView(int ou View)
setTit1e(string)
Altera o ttulo da action bar.
setIcon(DrawabIe)
setDisp1ayShowTit1eEnab1ed(boolean)
setDispIayShowHomeEnabied(boolean)
5.7 SearchView
A action bar permite adicionar views customizadas, sendo que um exemplo cls
sico o Searchview. Para este prximo exerccio, vamos utilizar o Searchview; assim,
quando o usurio tocar no boto de busca, o campo de texto ser expandido
para o usurio digitar o texto da busca. Felizmente o Searchview j faz todo esse
trabalho e para utiliz-lo basta congurar a tag android:actionViewCiass conforme
demonstrado a seguir.
/res/menu/menu_main.xmI
<menu xmins:android="http://schemas.android.com/apk/res/android"
xm1ns:toois="http://schemas.android.com/tools" >
<item android:id="@+id/action_search"
android:icon:"@drawabie/ic_action_search" android:title:"@string/action_search"
android:showAsAction="a1ways"
android:actionViewC1ass="android.widget.SearchView" />
// . . . Outros botes de refresh e settings aqui
Nota: o Searchview uma view customizada que pode ser inserida na action bar,
a qual chamada de Action View. Um Action View tem por objetivo substituir o
boto da action bar por alguma view customizada.
import android.widget.Searchview;
public class MainActivity extends Activity {
@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
getMenuInater().inate(R.menu.menu_main, menu);
Henulten item = menu.ndItem(R.id.action_search);
Searchview searchview = (Searchview) item.getActionView();
searchview.set0nQueryTextListener(onSearch());
return true;
}
@Override
public boolean onQueryTetChange(String newText) {
// Mudou o texto digitado
return false;
}
};
}
/res/menu/menu_main.xmI
<menu xmlns:androd="http://schemas.androd.com/apk/res/androd"
xmlns:tools="http://schemas.androd.com/tools" >
<tem androd:d="@+id/acton_search"
androd:icon:"@drawable/c_acton_search" androd:title:"@string/acton_search"
android:showAsAction="always" androd:actonVewC1ass="androd.wdget.SearchVew" />
ten androd:d="@+d/action_share"
androd:con="@drawable/c_acton_share" androd:tt1e="@strng/acton_share"
android:showAsActon="a1ways"
androd:actionProvderC1ass="android.widget.ShareActonProvder" /
<tem
134 Google Android - 4 edio
android:id="@+id/action_refresh" android:icon="@drawable/ic_action_refresh"
android:title="@string/action_refresh"
android:showAsAction="ifRoom" />
<item
android:id="@+id/action_settings" android:title:"@string/action_settings"
android:showAsAction="never" />
Nota: como desta vez existem trs botes na action bar, congurei os botes de
busca e compartilhar como showAsAction="always" para carem visveis, mas o boto
de refresh deixei como showAsAction="ifRoom"; assim, o Android vai decidir se o boto
vai car como action button ou se vai ser mostrado no menu action overflow.
MainActivity.java
import android.uidget.ShareActionProvider;
public class MainActivity extends Activity {
@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
// Searchview `
getHenuInater().inate(R.menu.menu_main, menu);
// ShareActionProvider
Henelten shareltem = menu.ndItem(R.id.action_share);
SharectionProvider share = (ShareActionProvider) shareltem.getActionProvider();
share.setShareIntent(getDefaultIntent());
return true;
Captulo 5 I Action Bar e temas 135
}
l
1w l
ShareActionProvder mostra todos os aplicativos que conseguem tratar a mensagem
enviada pela intent de compartilhamento, porm no simulador somente o aplicativo
de mensagem est instalado. Em um dispositivo real, comum encontrar vrios
z4 1
l
1I EKi. i1..'
aplicativos ao clicar no cone de compartilhar. Nesse exemplo vimos a configurao
y
.
!
I
E
' 1
= l l
de uma intent bsica para compartilhar textos. Futuramente, durante o desenvol
vimento do aplicativo para carros, veremos como compartilhar a foto de um carro.
1i
5
.
zTexto
I
1 ir , . _ . , _ .
para compartilhar
Feito isso. ao executar o projeto o resultado deve ser como a figura 5..lO. Na ver
dade, a split action bar economiza espao se necessrio; caso o dispositivo esteja
na horizontal, como existe espaco suciente, os botes da action bar cam na
parte superior normalmente.
* 'i L `*f. Oi kl
Nota: a configurao da split action bar deve ser feita activitv por activity e no
e possivel alterar pelo codigo, somente no arquivo Ai1ridMziiij~st.xml. Essa
conguraao parece que toi descontinuada (deprecated) no Material Design
pois no Android 5.0 isso no funciona mais. O print que voc est vendo e do
Android 4.4 com o tema Holo.
Captulo 5 n ction Bar e temas 137
5.10 Up navigation
No captulo 4, sobre activity criamos um aplicativo com uma tela de login com
a mensagem de bem-vindo, a qual foi mostrada em outra tela. Para voltar tela
anterior, podemos utilizar o boto voltar nativo do Android, ou o up navigation,
que a seta que aponta para a esquerda e ca na action bar.
Como j explicado anteriormente, o up navigation utilizado para subir na
hierarquia de telas; seu funcionamento muitas vezes parecido com o boto
voltar, mas, dependendo do caso, pode ser diferente. Vimos que para mostrar
a seta do up navigation na action bar basta chamar o mtodo getActionBar().
setDisp1ayHoneAsUpEnab1ed(true) e depois o mtodo on0ptionsItenSe1ected(Menulten)
chamado; o identicador android . R.id . home deve ser utilizado para identicar a ao.
@0verride
public boolean onMenuItenSe1ected(int featureld, Menulten item) {
// Clicou no "up navigation"
if(iten.getItenId() == android.R.id.hone) {
// O mtodo nish() vai encerrar essa activity
nish();
return true;
}
AndroidManfest.xmI
<?xn1 version="1.0" encoding="utf-8"?>
<nanifest ... >
<app1ication ... >
<activity android:nane=".MainActivity" android:1abe1="@string/tit1e_nain_activity" >
133 Google Android - 4 edio
</activity
<activity android:nane=".BenVindoActivity" android:parentActivityName="-HBHCVY" >
<!-~ Compatibilidade com a API levei 7+ -->
neta-data android:nane="android.support.PARENT_ACTIVITY"
android:va1ue=".HainActivity" /
/appiication
MainActivity.java
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.iayout.activity_main);
ActionBar actionBar = getActionBar();
actionBar.setTit1e("Capitu1o 5");
Captulo 5 I Action Bar e temas
MyTabLstener.java
import android.app.ActionBar;
import android.app.FragmentTransaction;
import android.content.Context;
import android.widget.Toast;
public class MyTabListener implements ActionBar.TabListener {
private Context context;
private int tabIdx;
public MyTabListener(Context context, int tabIdx) {
this.context = context;
this.tabIdx = tabIdx;
}
@0verride
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// Chamado ao selecionar uma tab
Toast.makeText(context, "Selecionou a tab: " + tabId, Toast.LENGTH_SHORT).show()
l
@0verride
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
// Chamado quando a tab perde o foco (se outra tab selecionada)
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// Chamado quando uma tab selecionada novamente.
l
}
HO
\11l`111.1
\
\111
._\
.1.\1`1 11c11.11'1111`111~
`\
1 1 1. _ ~1111 11111 1(\11\1
1 11 11111u.1 11 1c1111.11111 111*c c\c111|1111 111.11 .1 111 1.11 1 1
1I\\
1 _ 1.\1\( 111111111.111.1
\111`1l.1\1\1111111`1`11.11.11\ 1~1cc11111.111.1. N1|11'1111`1 1|111111111 111111 _1\x
1111119111 Androld 41 edi
. 111 . 11 .111
\\\ .11`111.1u\11;1c1.11111c111c.1u1.111:.111111111111c111111111111.1111.1 111.1 l~>1'1 * "* * *
1 1 1 \ `. . ` . 1.
'\
\l11(` \.111111 cs1u11.11 11111 1\1l1111\~111uu1' 1 1111 131 1 1
.~\111*11
1 11111.11 11111.1 1.11\1\ 11111111111111111.1111111111111111 1' 1 11.1111.11111.11 1111.11 11111. ll 111
1
1 1. . 111 111
1l111111\1\111`\`11`\\'1`1\1`1`I.11\11l1`|\1\111`1`1`11\1`1l111\1`l1\111l1l111l11.\111\11 1 1111111111 Il 1
1
.1-11.
Ateno: \'11c1* 11c\'c 1c1 |1c11'1~1111111 1`111` c\1s1c111 .11}11l1\ .11c1`1.1s \\\'.\l`l\ll\}S1 1111
1
111 11i1;11 11.15 l.111s. Isso 1\_\1Hl\`\`l` I`\11_\I11\` 1111 .~\11111'11111 1.11 11 111111111 11c 11.1\'1'1;.1c.111
p1^11111`111 11111111~ 1.1 111: c 111111.'.11111 1.11'.1 1111.11 1.I1\s. \`.111111s c11111.1 111 111.115 1.1111c
1111 111'1c11\'111\^1'l` U 1\l11\`-111\' 11115 1'.11'1'11. 11111111 1111111'.u' 11111111 1'1i.11` 1.111s \.\\111
.1 .11`1I1\11 1\.11`. 1110511111 1|111` 111`|W1`1'1`.111'1_1. 111115 CNIC 11\1~|\\}1|11 1~ 11111 11_\.111 11 \111'1`
app/buiId.gradIe
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultCong {
applicationld "br.com.livroandroid.actionbar"
ninSdkVersion 7 // API Level 7 (Android 2.1)
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
dependencies {
compile leTree(dir: 'libs', include: ['*.jar'])
// Dependncia da biblioteca de compatibilidade v7
conpile 'com.android.support:appconpat-v7:22.1.1'
1
No item defaulttong foi denido que a aplicao suporta o Android 2.1 (API Level 7)
como a verso mnima e o target o Android 5.1 (API Level 22). Lembre-se de que
o target deve ser sempre a ltima verso e quando voc estiver lendo este livro
1 42 Google Android - 4 edio
, . - z - ~ uivo dentro do
bem provavel que existam versoes novas. Na ultima linha dO 8fCl
item dependences configurada a seguinte dependencia:
Compile 'com.android.support:appcompat-v7:22.1.1'
Nota: ao adicionar uma nova dependncia no buildgradle clique no boto Sync Ilow
que aparece no editor. Isso faz com que o Gradle baixe a dependncia e a deixe
ativa no projeto, para que as funcionalidades como o assistente de cdigo e o
compilador encontrem as classes desta biblioteca. Essa opo tambm pode ser
acessada pelo menu Tools > Android > Sync Project with Gradle FIGS
/res/values/styIes.xmI
<resources
<sty1e name="AppTheme" parent="Theme.AppConpat.Lght.DarkActonBar">
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
public class MainActivity extends AppCompatActivity {
@Override
MainActivity.java
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.ShareActionProvider;
public class MainActivity extends AppCompatActivity {
@Override
144 Google Android - 4 edio
protected void onCreate(Bundie savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setTit1e("ActionBar Compat");
// Navegao por tabs
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
// Cria as tabs (Passa como parmetro o indice de cada tab: 1,2,3)
actionBar.addTab(actionBar.newTab().setTet("Tab 1")
.setTabListener(new MyTabListener(this,1)));
actionBar.addTab(actionBar.newTab().setTet("Tab 2")
.setTabListener(new MyTabListener(this,2)));
actionBar.addTab(actionBar.newTab().setTet("Tab 3")
.setTabListener(new MyTabListener(this,3)));
}
@0verride
public booiean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
getMenuInater().inate(R.menu.menu_main, menu);
// Searchview
Menultem searchltem = menu.ndItem(R.id.action_search);
Searchview searchview = (Searchview) HenuItemCompat.getActionView(searchltem);
searchview.set0nQueryTetListener(onSearch());
// ShareActionProvider
Menultem shareltem = menu.ndItem(R.id.action_share);
ShareActionProvider share = (ShareActionProvider)
Henultemtompat.getActionProvider(shareltem);
share.setShareIntent(getDefau1tIntent());
return true;
}
Como o exemplo que fizemos utiliza a navegao por tabs, precisamos ajustar
os imports da classe MyTabListener para utilizar a action bar de compatibilidade e
tambm a verso de compatibilidade da classe FragmentTransaction. Vamos estudar
mais detalhes sobre a classe FragmentTransaction no captulo 8, sobre fragments.
MyTabListener.java
import android.support.v7.app.ActionBar;
import android.support.v4.app.FragnentTransaction;
Captulo 5 I Action Bar e temas 145
public class MyTabListener implements ActionBar.TabListener {
// Mesmo cdigo aqui
}
Muito bem, estamos quase acabando. Para nalizar a migrao para a biblioteca
de compatibilidade, precisamos ajustar o arquivo XML de menu. No exem
plo que zemos utilizamos as tags android:showAsActon, android:actionVewClass e
androd:actionProvderC1ass para congurar os itens de menu e tambm o Searchview
e o ShareActionProvider.
/res/menu/main.xmI
<menu xmlns:android="http://schemas.androd.com/apk/res/android"
xmlnsztools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" tools:contet=".ManActvity">
<item androd:id="@+id/action_search"
android:icon="@drawable/c_acton_search" androd:title:"@string/action_search"
app:showAsActon="always"
app:actionVewC1ass="androd.support.v7.widget.Searchview" />
<item androd:id="@+id/action_share"
android:icon:"@drawable/ic_action_share" android:title="@string/action_share"
app:showAsActon="always"
app:actonProvderC1ass="android.support.v7.widget.ShareActonProvder" />
<item androd:id="@+d/action_refresh"
android:icon:"@drawable/c_action_refresh" android:title="@string/acton_refresh"
app:showAsActon="ifRoom" />
<item android:id="@+d/action_settings"
android:title="@string/action_settings"
app:showAsActon="never" />
http://developenandroid.com/guide/topics/ui/actionbart html
Android Training - Adding the Action Bar
https://developerandroid.com/training/basics/actionbar/index. html
Android Training - Styling the Action Bar
https://developerandroid.com/training/basics/actionbar/styling.html
, cAPruLo
it
Interface grca
tw gerenciadores de layout
\
6.1 View
147
148 Google Android - 4 edio
J 'Garod.app.u:t|vlty _
ii ~ ' c G android.view.VIeW
0 rndvnewayldodz ht):ari-dro|d.v1ew.`v'ew ._.___ _ __________._ ,.._. _ _
o sctConter\tVlew(leyoutRoslD: inn. vozd o dzowtev- WW 0'*'"3""*)* "'d
O setCmtent\=rew(\-rew: androd.v1ew.v|ew): void N 7
,\;. _'|' t Q af-0d_vl0w.V|QWGI'OtD
G an: oe! ex view Q <5dl0|ll.\MdQEl.ll'l'l5QBV|BW
VA\
- _ G andzro|d.wrd9et.AbsoluteLayout - \
'B android.wldqet.Button G ondroid.w\doet.EdrtTet ~ G nd|ozd.wdqet.Rt-latrvetayout
G andro|d.w|d1et.FrarneLayout
G .zndozd.widoe=t.LmeafLwUf
G androd.wdqez.rableia~/out
<LnearLayout mIns:androd="http://schemas.android.com/apk/res/android"
androd:1ayout_wdth="natch_parent" androd:Iayout_heght="natch_parent"
android:orentaton="verticaI" >
Nota: para testar os exemplos crie o arquivo XML de layout no projeto, e faa a
pr-visualizao do layout no editor visual. No ser necessrio executar o cdigo.
Ou, se preferir, abra o projeto deste captulo no Android Studio e abra cada arquivo
de layout no editor, assim car mais fcil para acompanhar as explicaes.
150 Google Android - 4 edio
Nesses arquivos de layout, vamos escolher FrameLayout como tag raiz, mas isso
na verdade nao importa. O primeiro exemplo demonstra >FFf'1@L>'0U Uflllfmd
(vsatrihtiuwsandroid:1ayout_widthtrandrod:1ayout_heightxnimdrwraD_Ctt-l5U
significa que a altura e largura d layout sera exatamente o espaco I1CCCSS8l`l()
para conter todos os componentes/views definidos na tela.
/res/layout/exempIo_wrap_content.mI
<?m1 version="1.0" encoding="utf-8"?>
<FrameLayout m1ns:android="http://schemas.androd.com/apk/res/androd"
androd:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
android:background="#8B8B83" >
<ImageVew
android:1ayout_wdth="wrap_content" android:1ayout_heght="wrap_content"
androd:src:"@drawab1e/android_green" />
Nesse exemplo, o Image-View utiliza o valor wrap_content para denir sua altura e
largura, consequentemente a imagem ocupa somente o tamanho necessrio. A
gura 6.2 mostra a pr-visualizao deste layout no editor.
Nota: sempre que car em dvida sobre qual o tamanho que o layout est
ocupando, utilize o atributo androd:background para denir uma cor para o fundo.
Neste caso estou utilizando a cor cinza, assim possvel visualizar a rea
retangular ocupada pelo layout.
/res/layout/exempIo_match__parent.xmI
<?xm1 verson="1.0" encodng="utf-8"?>
<FrameLayout m1ns:androd="http://schemas.androd.com/apk/res/android"
androd:1ayout_width="match_parent" android:1ayout_height="match_parent"
androd:background="#8B8B83" >
<ImageVew
android:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:src="@drawable/androd_green" />
A gura 63 mostra que desta vez o layout com fundo cinza ocupou a tela inteira e a
imagem ocupou apenas um pequeno espao necessrio para que fosse desenhada.
... .. -. ~.
~ -. z; ~~
' ' *' zhr* uma
7 `ilm*nte.
Dica: um atalho interessante do Android Studio c o Shtft+Sh1ft, qu* l
Janekapanivocechguarcuuquerrnnnechranquni)paral0LJh2 k)3
Agora vamos alterar tambm a largura e altura da imagem para I'1'CCh_Df@'f- 1550
signica que ela deve esticar e ocupar o tamanho ocupado por seu pal.
a- /res/layout/exempIo_match_parent_imagem.xml
O resultado dessa alterao pode ser visto na gura 6.4. Sempre que usar o valor
match_parent, os componentes vo esticar/expandir para preencher 0 tamanho do
layout-pai. Mas tome cuidado com imagens, pois geralmente elas devem ser dc
nidas como wrap_content, justamente para no distorcer a figura.
/res/layout/exempIo_textview_wrap_content.xmI
<?ml verson="1.0" encodng="utf-8"?>
<LnearLayout xm1ns:androd="http://schemas.androd.com/apk/res/androd"
android:1ayout_width="match_parent" androd:1ayout_height="match_parent"
androd:paddng="10dp" androd:orientaton="vertca1" >
<TetVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:tet="@strng/nome" />
<EditText
androd:1ayout_width="wrap_content" androd:1ayout_height="wrap_content"
android:nputType="tet" />
<BU'C'C0n
androd:layout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:1ayout_gravity="right" androd:text="@strng/ok" />
</LnearLayout>
/res/layout/exempIo_textview_wrap_content.xmI
<?xml version="1.0" encodng="utf-8"?>
<LinearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
android:1ayout_wdth="match_parent" android:1ayout_heght="match_parent"
androd:paddng="10dp" androd:orentation="vertcal" >
<TextView
android:layout_width="wrap_content" androd:layout_height="wrap_content"
androd:tet="@strng/nome" />
<EdtTet
androd:1ayout_wdth="natch_parent" androd11ayout_heght="wrap_content"
androd:nputType="tet" />
<Button
android:1ayout_wdth="wrap_content" android:1ayout_heght="wrap_content"
androd:1ayout_gravty="rght" androd:tet="@strng/ok" /
Fgunibb-(Mnqnnh'uwhunninuuh_unnwHruthuuuni
Captulo 6 I Interface grca - gerenciadores de layout 155
Note que no adicionamos valores xos para o tamanho, pois recomendado
criar telas que se ajustam automaticamente conforme a resoluo. Por isso no
importa se o aplicativo vai executar no smartphone, tablet ou numa gigante TV
que o layout ser o mesmo.
6.5 Framelayout
A classe androd.wdget.FrameLayout o mais simples de todos os gerenciadores de
layout e utilizada para empilhar uma view sobre outra.
possvel adicionar vrios componentes dentro do F rameLayout, e sempre os ltimos
componentes caro sobre os anteriores, seguindo o conceito de uma pilha, em
que o ltimo elemento ca no topo. O caso mais comum disso para inserir um
ProgressBar por cima de alguma outra view:A propsito, a classe ProgressBar uma view
que desenha aquela bolinha que ca girando para simular algum processamento.
Para exemplicar, vamos inserir um ProgressBar por cima do boto OK do formulrio
do exemplo anterior. Dessa forma, ao clicar no boto podemos disparar a animao
do Prog ressBar para simular o processamento de alguma tarefa. Vamos aprender
como fazer isso no cdigo depois, por enquanto vamos estudar apenas os layouts.
/res/layout/exempIo_frame_Iayout_1.xml
<?m1 version="1.0" encodng="utf-8"?>
<LnearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:layout_wdth="match_parent" androd:layout_heght="match_parent"
androd:orentaton="vertca1" androd:paddng="19dp" >
<TetVew androd:1ayout_wdth="wrap_content" androd:layout_height="wrap_content"
androd:tet="@strng/nome" />
<EdtText androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
androd:inputType="tet" />
<FrameLayout android:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content" >
<Button
androd:layout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="rght" androd:text="@string/ok" />
<ProgressBar androd:Iayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center" />
</LnearLayout>
* -s.,
z
/res/layout/exempIo_frame_Iayout_2.mI
<?xm1 verson="1.0" encodng="utf-8"?>
<FrameLayout xmlns:androd="http://schemas.android.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:orientaton="vertca1" androd:paddng="10dp" >
LStVew
androd:d="@+d/Istvew"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent" />
ProgressBar
android:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravity="center" />
A figura 6.8 mostra a pr-visualizao desse layout. O editor mostra urna lista para
simular que o Lstvew est preenchido. j o ProgressBar utilizado para mostrar a
animao para o usurio, enquanto os dados da lista esto sendo carregados. O
ProgressBar est por cima do Lstvew, pois isso que o FrameLayout faz_
Captulo 6 I Interface grca - gerenciadores de layout
ltm 1
o..
mzrl THY K
R2_
fzti ima Z
Item 3
`S|.'l' i
.lif Q*
lte-m 4
sua trr'n -1
Item 5
tltl Hr-m S
'Item 5
au: mais l
Item 7
6.6 LinearLayout
A classe androd .wdget. LinearLayout um dos gerenc1adores de layout ma1s utilizados
sendo possvel organizar os componentes na horizontal (padrao) ou na vertlcal
A orientao, nesse caso, configurada pelo atr1buto androtd ortentatton
A seguir temos um exemplo de como utilizar a classe L1neacLayout
/res/layout/exemplo_linear_layout_1 xml
<?xnl verslon="1.0" encodng="utf-8"?>
<LnearLayout xnlns:androd="http://schenas androtd com/apk/res/androtd
androd:layout_wdth="natch_parent" androtd layout hetght match parent
androd:ortentation="horlzontal ou verttcal
androd:paddng="10dp" >
<ImageVew androd:src="@drawable/androtd blue
androd:layout_wdth="wrap_content androtd layout hetght wrap content
androld:contentDescrpton="@strng/content descrtptton /
<InageVew androd:src="@drawable/androtd green
androd:layout_wldth="wrap_content androtd layout hetght wrap content
androd:contentDescrpton="@string/content descrtptton /
Google Android - 4 edio
^A
158
''IPI|
Pa|e[[e- 'UPene
V' N Of! ' Vl ev2palee
Pxus NexusV One
_ ' ll
exem0l0_l|near_laY0ut_1 xml 2 ' 0mDl0-l''UY0Ul- 1 *ml i
I'I
Ab Small Text
Small Text
l* Button
Button ;~ vw
Q .:'l.
.r Text Fields
. Toczqle8utton
3 V
fl cneciB0
Lv~\= _.
C recl<8o
ToggleButton gi z?
Repare que o ProgressBar est declarado com o atributo androd :layout gravty="center",
o qual faz com que ele seja posicionado no centro do seu layout-pai. Para demons
trar outras maneiras de utilizar o atributo androdzlayout gravty, criaremos uma
Q ue
tela com trs imagens inseridas na vertical, a in a as
cam l` hna
d esquerda, no
centro e na direita da tela.
Captulo 6 I Interface grca - gerenciadores de layout 159
/res/layout/exempIo_Iinear_Iayout_2_gravity.xmI
<?xm1 verson="1.0" encodng="utf-8"?>
<LnearLayout xmlns:android="http://schemas.android.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:1ayout_height="match_parent"
android:orentaton="vertica1" >
<ImageVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:1ayout_gravity="1eft" androd:src="@drawable/androd_green" />
<InageVew
android:1ayout_wdth="wrap_content" android:1ayout_heght="wrap_content
androd:1ayout_gravty="center" androd:src="@drawab1e/androd_b1ue" />
<ImageVew
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content"
android:1ayout_gravty="right" androd:src="@drawab1e/androd_green" />
E1
Enviar
2
/res/layout/exempIo_linear_Iayout_4_weight.xmI
<?xm1 version="1.0" encodng="utf-8"?>
<LinearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:orentaton="vertica1"
androd:layout_wdth="match_parent" android:layout_heght="match_parent" >
<EdtTet.androd:1ayout_weght="1"
andrd:layout_wdth="match_parent" android:1ayout_height="0dip"
android:text="Texto (weght=1)" />
<EditTet android:1ayout_weght="2"
androd:1ayout_wdth="match_parent"
android:1ayout_height="0dip"
android:text="Texto (weight=2)" />
<EdtText androd:1ayout_weight="3"
android:layout_wdth="match_parent" androd:1ayout_height="0dp"
android:tet="Texto (weght=3)" />
|'QI1O (vnil=2l
Iulo (miqti-3)
Para entender melhor o que aconteceu, vamos fazer uma superconta para somar
estes valores: 1 + 2 + 3 = 6. Como a soma dos valores do atributo 1ayout_weght ,
cada campo de texto vai ocupar o espao proporcional na tela, relativo ao total.
como se 6 fosse 1.000/0. Assim, podemos visualizar que o ltimo campo denido
como 1ayout_weght=3 ocupou metade da tela, sendo que 3 50% de 6.
Para entender melhor como a atribuio de pesos funciona, vamos brincar um
pouco com esse exemplo, alterando o cdigo-fonte do arquivo XML de layout.
Por exemplo, vamos alterar o primeiro campo de texto para peso = 3. Nesse caso.
agora a soma ficaria 3 + 2 + 3 = 8 (Figura .13). Como o primeiro e o terceiro
campo tm os mesmos pesos, eles ocuparo o mesmo espao na tela. j o campo
do meio ser o menor.
Para nalizar este tpico, vou dar uma dica muito importante e que muitos desen
volvedores Android demoram a descobrir ou entender. O componente Lstvew que
utilizado para criar listas deve ser utilizado na maioria das vezes com peso = 1
na vertical para esticar a sua altura.
<LstVew
androd:d="@+d/Iistvew"
androd:1ayout_wdth="match_parent"
androd:1ayout_heght="@dp" androd:1ayout_weght="1" />
Captulo 6 n Interface grca - gerenciadores de layout 153
A diferena que, se congurarmos a altura como androd : layout_heght="match_parent",
a lista vai esticar sem pensar em quem est abaixo dela, mandando todas as outras
views para fora da tela. Mas com a configurao do peso, o Ltstvew vai esticar
at onde puder, pois geralmente somente ele tem o peso = 1, ou seja, somente o
Lstvew deve esticar. medida que formos avanando na leitura deste livro, vamos
aprender mais detalhes e voltamos a revisar este conceito.
Y mw (weight =3}
Texto (wegm<=2} 3
Texto (wegzm=3}
/res/layout/exempIo_tab|e_Iayout_shrink.xm|
<Tab1eLayout xmins:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:shrinkCo1umns="2">
eu -_c_,_13_33_9.12s9_U_?L$l Li
Coluna ICo|una 2CoIuna. 3
/res/layout/exempIo_tabIe_layout_stretch.xmI
<Tab1eLayout xmlns:androd="http://schemas.android.com/apk/res/androd"
androd:1ayout_wdth="match_parent"
androd:layout_heght="match_parent"
androd:stretchCo1umns="1" >
android:1ayout_heght="2dp"
androd:background="#FF90909@" />
android:layout_gravty="rght"
androd:text="@string/reas_100" />
androd:layout_gravty="rght"
androd:tet="@strng/reas_200" />
androd:1ayout_gravty:"rght"
androd:tet="@string/reais_300" />
<Vew
androd:1ayout_heght="2dp"
androd:background=#FF909090" />
<LnearLayout
androd:1ayout_wdth="wrap_content"
androd:1ayout_heght="match_parent"
androd:gravty="bottom|center_horzonta1" >
<Button
androd:1ayout_wdth="wrap_content"
androd:1ayout_heght="35dp"
android:text="@strng/cancelar" />
<Button
androd:1ayout_wdth="wrap_content"
androd:1ayout_heght="35dip"
androd:tet="@strng/comprar" />
</LnearLayout>
-.
Cancvm Comum
/res/layout/exempIo_table_layout_form.xml
<?ml version="1.0" encoding="utf-8"?>
<Tab1eLayout xmins:android="http://schenas.android.con/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:stretchCo1umns="1">
<TableRow android:gravity="right">
<Button android:id="@+id/login" android:text="Logn" />
168 Google Android - 4 edio
Lihserve qturziiihinia hria usa o atrvuto androd:graV1tY= flght Para al*
iihar o bcHtkrlogin direua.( can1pth:scr1a COHICH1 0 arrvuro
androd:nputType="tetPassword" para mostrar os no lugar do texto (Flgf -J.
Senha
Login
6.13 GridLayout
A classe androd.wdget.GrdLayout organiza as views em linhas e colunas; para isso
a view deve utilizar os atributos 1ayout_row e 1ayout_co1umn.
No exemplo a seguir vamos criar quatro botes. Veja que o layout foi denido
com 2 linhas e 2 colunas e cada view inserida em determinada posio.
/res/layout/eemp|o_grid_Iayout.mI
<?m1 verson="1.0" encodng="utf-8"?>
<GrdLayout xm1ns:androd="http://schemas.android.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:1ayout_height="match_parent"
android:padding="16dp"
androd:co1umnCount="2" android:rowCount="2">
<Button
android:1ayout_wdth="wrap_content" androd:1ayout_height="wrap_content"
android:1ayout_co1umn="0" androd:1ayout_row="0"
android:tet="Boto 1" />
<Button
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:1ayout_co1umn="1" androd:1ayout_row="6"
androd:tet="Boto 2" />
Captulo 6 n Interface grca - gerenciadores de layout 159
<Button
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:Iayout_co1umn="0" androd:1ayout_row="1"
androd:tet="Boto 3" />
<Button
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_co1umn="1" androd:1ayout_row="1"
androd:tet="Boto 4" />
</GrdLayout>
O GrdLayout tambm contm alguns atributos que no usei nesse exemplo, como
os atributos androd:1ayout_co1umnSpan e androd:1ayout_rowSpan, os quais podem ser
utilizados para que a view ocupe mais de uma coluna ou linha, de forma similar ao
span em pginas I-ITML. Voc tambm pode usar o atributo androd:1ayout_gravty
para informar se a view deve esticar na vertical, horizontal ou ambas (fi11_vertca1,
11_horzonta1 e 11). O resultado do exemplo pode ser visualizado na gura 6.11
.............
,mni i-aowz_
*sim
/res/layout/exempIo_grid_|ayout.xmI
<?xm1 version="1.0" encodng="utf-8"?>
<android.support.v7.widget.GrdLayout m1ns:androd="http://schemas.androd.com/apk/res/androd'
xmlns:app="http://schemas.androd.com/apk/res-auto"
androd:1ayout_width="match_parent" androd:1ayout_height="match_parent"
170 Google Android - 4 edio
androd:padding="16dp"
app:co1umnCount="2" app:rowCount="2">
<BUtt0n
androd:1ayout_width="wrap_content" androd:1ayout_height="WF3P_C"tt"
androtd:tet="Boto 1"
app:1ayout_co1umn="0" app:1ayout_row="0" /
</androd.support.v7.widget.GridLayout>
6.14 Relativelayout
A classe androd.widget.Re1atveLayout pode posicionar os componentes ao lado,
abaixo ou acima de outro componente j existente. Para isso necessrio denir
um id para cada componente da tela, pois o posicionamento de um componente
depende de outro. Os seguintes atributos podem ser utilizados para informar a
posio do novo componente inserido relativo ao componente j existente:
Atributo Descrio
anroztzyoutzbiori Posiciona abaixo do componente indicado.
androd/Iayoutzabove Posiciona acima do componente indicado.
android:1ayout:toRght0f Posiciona direita do componente indicado.
androd/1ayout:toLeftOf Posiciona esquerda do componente indicado.
androd:1ayout_a1gnParentTop Alinha no topo do layout-pai.
android:1ayout_a1ignParentBottom Ainha abaixo do layout-pai.
android:1ayout_a1gnParentRght Alinha direita do layout-pai.
android:1ayout_a1ignParentLeft Ainha esquerda do layout-pai.
android:1ayout_a1ignTop Alinha no topo do componente indicado.
androd:1ayout_a1gnBottom Ainha abaixo do componente indicado.
androdzlayout a1ignRight Alinha direita do componente indicado.
androidzlayout a1i.gnLeft Ainha esquerda do componente indicado.
androd:1ayout_margnTop Utilizado para denir um espao na margem superior
do componente.
androidzlayout margnBottom Utilizado para denir um espao na margem inferior
do componente.
android:1ayout_marginRight Utilizado para denir um espao direita do compo
nente.
/res/layout/exempIo_reIative_|ayout_form.xml
<?nl version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:background="#8B8B83" android:padding="16dp">
<TetView android:id="@+id/labelUsuario"
android:layout_width="55dip" android:layout_height="wrap_content"
android:tet="@string/usuario"/>
<EditText android:id="@+id/canpoUsuario"
android:layout_width="natch_parent" android:layout_height="wrap_content
android:background="@android:drawable/editbox_background"
android:layout_toRight0f="@id/labelUsuario"
android:inputType="text"/>
<TextView android:id="@+id/labelSenha"
android:layout_width="55dip" android:layout_height="wrap_content"
android:layout_below="@id/campoUsuario"
android:gravity="left" android:tet="@string/senha"/>
<EditText android:id="@+id/canpoSenha"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_toRight0f="@id/labelSenha"
android:layout_alignTop="@id/labelSenha"
android:inputType="textPassword" />
usuario
Senha
Lql
/res/layout/exemplo_linear_layout_form_aninhado.xml
<?xnl verson="1.0" encoding="utf-8"?>
<LinearLayout xmlns:androd="http://schenas.android.con/apk/res/android"
android:orentation="vertica1"
android:layout_wdth="natch_parent" android:1ayout_heght="match_parent" >
<TetVew
android:layout_width="wrap_content" androd:layout_height="wrap_content"
androd:text="@strng/none" />
<EdtTet
android:layout_width="match_parent" android:1ayout_height="wrap_content"
android:textColor="#ff0000" androd:inputType="text"/>
<TetVew
174 Google Android - 4 edio
android:layoutmwidthz"wrapucontent" androd:layout_he9hf:"WfaD~Ctet"
androd:text="@strng/senha" />
<EdtTet androd:inputType="textPassword
androd:layoutwdthz"match_parent" android:layout_heght="WF8D_Ct@t" />
LinearLayout android:orentation="horizonta1"
androd:layout_wdth="match_parent androd:layout_h9h="Wf6D_C0@t"
androd:background="#cccccc" androd:gravty=center">
<Button androd:layout_wdth="wrap_content" androd:layout_height="wrap_content"
androd:text="@string/cancelar />
Button androd:1ayout_wdth="wrap_content" androd:layout_he9ht="WFD_C0"@"
androd:text="@strng/login" />
</LnearLayout
</LnearLayout>
A pre-visualizao dessc layout pode ser vista na figu ra (mil). () LinearLayout principal
da tela e vertical, mas no final foi utilizado um LinearLayout horizontal para comer
s dois lvotes. Observe que layut que contem os dois botes foi definido corn
uma cr de fundo cinza para demonstrar seu tamanho.
F@mu%-Umuuymtmmmr
Para adicionar uma View dentro do layout, utilizado o mtodo addView(view), que
recebe uma subclasse de android.view.View, como Textview, EditText, Inageview, Button
etc. Para testar essa activity abra o projeto deste captulo no Android Studio e
execute no emulador.
ExempIoLinearLayoutAP|Activity.java
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
public class ExemploLinearLayoutAPIActivity extends Activity {
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Cria o layout
LinearLayout layout = new LinearLayout(this);
layout.set0rientation(LinearLayout.VERTICAL);
layout.setLayoutParans(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
layout.setPadding(10, 10, 10, 1@); // Espaanento em pixels
TetView nome = new TextView(this);
nome.setTet("None:");
nome.setLayoutParams(new LayoutParams(LayoutParans.wRAP_CONTENT,
LayoutParams.wRAP_CONTENT));
layout.addView(nome);
EditTet tnome = new EditText(this);
tnone.setLayoutParams(new LayoutParans(LayoutParans.MATCH_PARENT,
LayoutParams.wRAP_CONTENT));
layout.addView(tnome);
// Focus
tnome.requestFocus();
Textview senha = new TextView(this);
senha.setTet("Senha:");
senha.setLayoutParams(new LayoutParams(LyoutParams.wRAP_CONTENT,
LayoutParams.wRAP_CONTENT));
layout.addView(senha);
EditTet tsenha = new EditText(this);
tsenha.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.wRAP_CONTENT));
layout.addView(tsenha);
176 Google Android - 4 edio
// Boto alinhado a direita
Button ok = new Button(this);
ok.setLayoutParams(new LayoutParams(LayoutParans.NRAP_CONTENT,
LayoutParams.NRAP_CONTENT));
ok.setGravity(Gravity.RIGHT);
ok.setText("0K");
layout.addView(ok);
// Informa o layout que foi criado pela API
setContentView(layout);
}
Como executar esse exemplo vou deixar por sua conta, pois a nica coisa que ele
faz criar um simples formulrio. Mas saiba que possvel criar todas as telas
utilizando somente a API: basta usar as subclasses de View desejadas. Os mtodos
das classes so semelhantes (quase sempre) aos atributos do XML. Por exemplo, a
classe Textview tem o mtodo setText(x), que corresponde ao atributo android : text do
XML. Fica a seu critrio escolher a maneira mais adequada para criar a interface
grca. Entretanto, criar o layout em XML a forma recomendada pelo Google,
pois separa a interface grca da lgica de negcios.
ExemploTabIeLayoutAPIAttivity.java
inport android.widget.Tab1eLayout;
import android.widget.TableLayout.LayoutParans;
public class EemploTableLayoutAPIActivity extends Activity {
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
Captulo 6 I Interface grca - gerenciadores de layout
// Cria o layout
TableLayout tabela = new TableLayout(this);
tabela.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// Expande a coluna 1
tabela.setColumnStretchable(1, true);
// Linha 1
TableRow linhal = new TableRow(this);
TetView nome = new TetView(this);
nome.setText("Nome:");
linha1.addView(nome);
EditTet tnome = new EditText(this);
// Focus no campo nome
tnome.requestFocus();
linha1.addView(tnome);
// Linha 2
TableRow linha2 = new TableRow(this);
Textview senha = new TetView(this);
senha.setTet("Senha:");
linha2.addView(senha);
EditTet tsenha = new EditTet(this);
tsenha.setTransformationMethod(new PasswordTransformationMethod());
linha2.addView(tsenha);
// Linha 3
TableRow linha3 = new TableRow(this);
linha3.setGravity(Gravity.RIGHT);
// Boto alinhado direita
Button ok = new Button(this);
ok.setText(" Login ");
linha3.addView(ok);
// Adiciona as linhas
tabela.addView(linha1);
tabela.addView(linha2);
tabela.addView(linha3);
// Informa o layout
setContentView(tabela);
}
/res/Iayout/actvity_exempIo_scroIIview.xm|
<ScrollView nlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="natch_parent" android:layout_height:"wrap_content">
<LinearLayout android:id="@+id/layoutl"
android:layout_width="natch_parent" android:layout_height:"wrap_content"
android:orientation="vertical">
/LinearLayout
/ScrollView
Exemp|oScroIIViewActivity.java
public class EenploScrollViewActivity extends Activity {
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getActionBar().setDisplayHoneAsUpEnabled(true);
setContentView(R.layout.activity_exenplo_scrollview);
LinearLayout layout = (LinearLayout) ndViewById(R.id.layout1);
for (int i = 0; i < 100; i++) {
Textview text = new TextView(this);
// obrigatrio o layout_width e layout_height
tet.setLayoutParams(new LayoutParams(LayoutParans.NRAP CONTENT,
LayoutParans.HRAP_CONTENT));
tet.setTet("Texto: " + i);
layout.addView(text);
}
}
Captulo 6 I Interface grca - gerenciadores de layout 179
A figura 6.20 mostra o resultado do exemplo, em que podemos ver que foi criada
a barra de rolagem devido grande lista de elementos adicionados no layout.
Esse exemplo tambm interessante, uma vez que mostra como recuperar o
LinearLayout do arquivo XML e adicionar vrias views dinamicamente pela API.
No prximo tpico vou explicar algo importante sobre a action bar e navegao
de telas, portanto preste ateno nestas duas guras. Voc deve ter percebido
que a primeira gura referente ao projeto de exemplo deste captulo, no qual a
ManActivty mostra todos os exemplos na lista.
Texto: T2
_ ex o 18
Texto: 15
Texto: 17
ImageSw|tcher Texto;TIQ
t
Texto; 20
/res/layout/inate_tetview.xmI
<TetView mlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textview"
android:layout_width="wrap_content" android:layout_hetght="wrap_content"
/
Note que este arquivo de layout contm o Textview que estvamos instancianclo
por programao no exemplo anterior. Desta vez esse mesmo Textview ser ins-
tanciado diretamente por meio do arquivo XML, procedimento que conhecido
pelos desenvolvedores Android com inflar um XMI lsso feito cont a classe
Layoutlnater, conforme demonstrado a seguir:
tia ExempIoScroIIVlewActivity.java
/res/values/strings.xmI
<?xm1 version="1.G" encodng="utf-8"?>
<resources
strng name:"app_name"LvroAndrodCap7-View</strng
<strng name="he11o_wor1d">He11o worId!</strng
<strng name="acton_settngs">Settngs</5trn9>
182
Captulo 7 nilnterface grca - View 133
<strng name "msg_verde_e_branco">Teto verde e branco </strng>
<string name "msg_azu1_e_branco">Texto azul e branco</strng>
<strng name "msg_verme1ho_e_branco">Teto vermelho e branco</strng>
<strng name "teto1">Teto campo 1 azul
Para acessar essas mensagens no cdigo Java, utilize a classe R com a sintaxe
R.strng.chave_mensagem.
Para traduzir os textos para diferentes idiomas, basta criar uma pasta /res/values
-idioma.
/res/values/values-pt/stringsxml
Caso queira deixar claro que esta pasta para o portugus do Brasil, adicione
a regio.
/res/values/values-pt-rBR/stringsxml
/res/values/coIors.xmI
<?xml verson="1.0" encodng="utf-8"?>
<color name:"vermelho">#ff0000
<color name="azul">#00@0ff
<color name="verde">#00ff00
<color name:"branco">#ffffff
. . , . z - f . etCo1or cor _
<color name="azu1">#500000ff
Para utilizar uma cor no codigo, utilize o metodo getRe50UrC'S() 9 ( )
Textvew t = (Textview) ndVewById(R.d.tet);
t.setTextCo1or(getResources().getCo1or(R.co1or.branco));
t.setBackgroundCo1or(getResources().getColor(R.co1or.verde));
t.setTet(R.string.msg_verde_e_branco);
''I..,
ja ouviu falar de CSS em pginas para internet? No Android, existe algo parecido e so
chamados de esnlos. Um tema que estudamos anteriormente um conjunto de estilos
Com um arquivo de estilo, podemos denir de uma s vez determinado padro
de cor, assim como o tipo da fonte como negrito e itlico Dessa forma possvel
dar um nome a esse estilo e usa-lo no codigo. Um arquivo de estilos tambm pode
ter qualquer nome, desde que seja formado pelas tags <sty1e name="nomeEst1o" c a
tag <1tem> para compor cada elemento.
Captulo 7 I *Interface grca - View 185
/res/values/css.xml
<?ml version="1.0" encoding="utf-8"?>
<style name="estiloEemplo">
<item name android:textSize">14sp
<item name android:textColor">#ffffff
<item name "android:background">#ff00G0
<item name android:textStyle">italic
Se voc quiser, possvel criar uma activity com o seguinte cdigo para testar os
exemplos. No entanto, como vou utilizar os arquivos XML de layout para demons
trar, a prpria pr-visualizao do editor j suficiente para entender os exemplos.
ExemploTextoCoresActivity.java
TetView style:"@sty1e/estiloxempio"
android:iayout_width="match_parent" android:1ayout_height="wrap_content"
android:iayout_marginTop="10dp"
android:text:"@string/msg_verme1ho_e_branco" />
</LinearLayout
/res/layout/exempIo_edittext.xmI
<?xn1 verson="1.G" encoding="utf-8"?
<Tab1eLayout xmlns:android="http://schenas.android.con/apk/res/android"
android:1ayout_width="natch_parent" android:1ayout_height="match_parent"
android:stretchCo1unns="1" android:padding="16dp">
7.7 AutoCompIeteTextView
A classe android.widget.AutoCompleteTetView um campo de texto que completa
automaticamente o texto que o usurio est digitando, e muito til em deter
minados casos. Existem dois parmetros que podem ser informados no momento
de criar essa classe.
ExemploAutoCompleteTextViewActivity.java
~
Nesse exemplo. o atributo attdroidzconplettonthreshold toi dettido mm o valor I.
indicando que o popup mm o autopteetteltiatento deve ser aberto quando o usa
ario digitar a primeira letra. U atributo androidzcompletionint delitte .t tttensagettt
que apate\\~ tia parte mt~z~o~ do popitix .~\ ligura 7.2 exibe o resultado. Como lot
digitado o texto l`ar` os estados Para. Paraiba e i`arati.i tiiram sugeridox
1a301
.QfW*a@
_,._~1.; \ :. zzt- _.'5.~~.zzwz.zirz:t@:zi;t~
ti .z'.~,f~,z'-.;,,_ t z -v..r .., z~-. -. :
....'3'
" _*
' 4_I
_.~_.z
_ `L * *f
-. . *L-~
* `N" \'Vll'*
,. ' 'z i, Six*
rz~.iii,
-.z; 5 -~.\. ..
;_-'*':: 5g"
' :g1Z
__..._ _.va ' V r -.,_. z
os exemplos. `
pari! nvlhf UHPHhu\`l\\ da `\x(\`*utt\ `\ \\'i(\\ dv (\\(\`l\ln
deste capitulo no etnttlador. A aetwity ittieial eotttettt tuna Iist=wz.~tii ootit todos
Captulo 7 I Interface grca - View 191
7.8 Button e ImageButton
As classes android.widget.Button e android.widget.InageButton so utilizadas para criar
um boto na tela. A diferena que a classe InageButton permite usar uma imagem
para desenhar o boto. O exemplo a seguir utiliza a classe ImageButton, que exibe
um alerta quando o boto clicado.
ExempIoImageButtonActivity.java
public class ExemploInageButtonActivity extends Activity {
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exenplo_inage_button);
ImageButton botaolmageml = (InageButton) ndViewById(R.id.ing1);
nal Context context = this;
botaolmageml.set0nClickListener(new View.0nClickListener() {
public void onClick(View v) {
Toast.nakeTet(context, "Imagem 1 OK", Toast.LENGTH_SHORT).show();
}
});
InageButton botaoImagem2 = (ImageButton) ndViewById(R.id.img2);
botaoInagem2.setImageResource(R.drawable.snile2);
botaoImagem2.set0nClickListener(new View.0nClickListener() {
public void onClick(View v) {
Toast.makeText(contet, "Imagem 2 OK", Toast.LENGTH_SHORT).show();
}
});
}
/res/Iayout/activity_exempIo_imagem_button.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xnlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<InageButton android:id="@+id/ingl" android:layout_width="natch_parent"
android:layout_height="wrap_content" android:src="@drawable/snilel" />
<ImageButton android:id="@+id/ing2" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
192 Google Android - 4 edio
Observe que a primeira imagem denida diretamente no QYQUIVO de layout COm
o atributo androd:src="@drawab1e/sm1e1". Entretanto, a segunda imagem e definida
dinamicamente no cdigo.
botaoImagem2 . setImageResource(R.drawab1e. smi1e2);
O arquivo XML dene uma imagem para cada tipo de estado do boto. Portanto,
voc precisa pedir aos designers para criar essas imagens, ou pelo menos duas:
normal e selecionado. Com esse arquivo de seletores (selectors) em mos, basta
utiliz-lo como a imagem de fundo para o boto. Na prtica, o arquivo XML
vai virar uma imagem e voc pode utilizar as notaes @drawab1e e R.drawable para
acessar esse recurso.
Outra classe que pode ser utilizada para selecionar uma opo, similar ao
checkbox, a androd.wdget.ToggleButton.
<ToggleButton androd:id="@+id/toggle"
androd:layout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:textOn="Lgado"'android:tet0ff="Desligado" />
194 Google Android - 4 edio
Essa classe tambm contm o mtodo isChecked(). Com OS 8ITbUf05 '3dfd5tet9"
recem no boto quando ele
e androidztextff, possvel controlar os textos que apa I d 1
est selecionado ou no. A seguir, podemos visualizar um exemp o as C asses
CheckBo e ToogleButton.
/res/layout/activity_exempIo_toogIe_button.xmI
?nl version="1.6" encoding="utf-8"?
<LinearLayout mlns:android="http://schemas.android.com/apk/rES/dfd"
android:layout_width="match_parent" android:layout_height="matCh_P3rent"
android:orientation="vertical">
<TetView
android:layout_width="wrap_content" android:layout_height="wrap_content
android:text="Exemplo de CheckBox e ToggleButton" />
<CheckBo android:id="@+id/check1"
android:layout_width="wrap_content" android:layout_height="wrap_content
android:text="Check 1" />
<CheckBo android:id="@+id/check2"
style="?android:attr/starStyle"
II
android:layout_width="wrap_content" android:layout_height="wrap_content
android:text="Check 2" /
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="0 ToggleButton mostra os textos Ligado ou Desligado..." /
<ToggleButton android:id="@+id/toggle"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet0n="Ligado" android:tet0ff="Desligado" />
<Button android:id="@+id/bt0K"
android:layout_width="wrap_content" android:layout_height=wrap_content"
android:tet="0K />
</LinearLayout
ExempIoToggIeBut1onActivity.java
});
}
' Desligadb
'mc
l
/res/values-v21/styIes.xmI
<?xml version="1.0" encoding="utf-8"?>
iten name="android:co1orPrinary">@co1or/primary
196 Google Android - 4 edio
_ _ . . . extual a bars -->
<!~- Vartaao escura da cor primaria para a status bar e cont PP
<iten nane="android:colorPrinaryDark"@color/prn8fY-fk<it">
<color nane="prinary">#63A9F4
<color name="prnary_dark">#61579B/color>~
<color nane="accent"#F44336 <!~- Vermelho -->
<color name="vermelho">#ff00G0
<color name="azul">#0@09ff
<color name:"verde">#00ff09
<color name="branco">#ffffff
Nota: no captulo 11, sobre Material Design, vamos voltar a estudar essas cores.
De qualquer forma, importante voc entender que a cor primria (primary)
representa a cor da marca do cliente ou do seu aplicativo, e a cor de acentuao
(accent) utilizada para destacar as views e determinados componentes na tela.
7.10 RadioButton
O componente radio button permite selecionar apenas uma nica opo de uma
lista. No Android, as classes androld.wdget.Radoroup e androd.wdget.RadioButton
so utilizadas para isso. A classe RadoGroup dene o grupo que contm a lista de
opes, na qual cada opo representada por um RadoButton.
O exemplo a seguir cria dois botes com os textos Sim e No, respectivamente:
<RadioGroup androd:layout_width="match_parent"
BHFO113)/U_h@9ht="W`6D_Content" androd:orientation="horizontal"
Captulo 7 n Interface grca - View 197
androd:d="@+d/group1">
<RadoButton androd:d="@+d/radoSm"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:tet="Sm" androd:checked="fa1se" />
<RadoButton androd:d="@+d/radioNao"
androd:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:tet="No" androd:checked="fa1se"/>
</RadoGroup>
Observe que cada RadoButton tem um id, para que posteriormente o id do boto
selecionado possa ser recuperado, chamando o mtodo getCheckedRadoButtonId()
da classe Radoroup. Nesse exemplo, foram denidos os ids radoSim e radoNao. Isso
possibilita utilizar o seguinte cdigo para descobrir qual boto foi selecionado:
boolean sim = R.d.radoSm == group.getCheckedRadoButtonId();
/res/Iayout/activity_exempIo_check_radio_form.xmI
<?xml verson="1.0" encodng="utf-8"?>
<LnearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:orientaton="vertca1">
<TetVew
android:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content
androd:text="Nome" />
<EdtText androd:d="@+id/tetNome"
androd:1ayout_wdth="match_parent" androd:1ayout_height="wrap_content" />
<TextView
androd:1ayout_wdth="wrap_content" androd:1ayout_height="wrap_content"
android:tet="Concorda?" />
<RadoGroup androd:1ayout_width="match_parent"
android:iayout_heght="wrap_content" androd:orentaton="horzonta1"
androd:d="@+d/group1">
<RadioButton androd:d="@+d/radoSim"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content
androd:text="Sm"
androd:checked="fa1se" />
<RadioButton android:d="@+d/radioNao"
androd:1ayout_wdth="wrap_content" android:1ayout_height="wrap_content"
android:tet="No"
androd:checked="fa1se"/>
198 Google Android - 4 edio
<TetView
android:layout_width="wrap_content" android:layout_height="wFD_C"t@"t"
android:tet="Receber Email ?" />
<CheckBo android:id:"@+id/checkReceberEmail" H
android:layout_width="wrap_content" android:layout_height= wFD_Cf9"t
android:tet="Receber email" />
<Button android:id="@+id/buttonEnviar"
android:layout_width="wrap_content" android:layout_height="wrP_C0t@"t
android:tet="Enviar" />
ExemploCheckRadioFormActivity.java
public class EemploCheckRadioFormActivity extends Activity {
private static nal String TAG = "livro";
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eenplo_check_radio_form);
nal EditText tetNone = (EditTet) ndViewById(R.id.tetNone);
nal RadioGroup group = (RadioGroup) ndViewById(R.id.group1);
group.set0nCheckedChangeListener(new Radioroup.0nCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedld) {
boolean sim = R.id.radioSim == checkedldg
boolean nao = R.id.radioNao == checkedld;
if (sin) {
Log.i(TAG, "Marcou radio Sim: " + checkedld)
} else if (nao) {
Log.i(TAG, "Marcou radio No: " + checkedld)
}
});
nal CheckBo check = (CheckBo) ndViewById(R.id.checkReceberEnail);
// Dene um listener para executar quando alterar o check
check.set0nCheckedChangeListener(new CheckBo.0nCheckedChangeListener() {
public void onCheckedChanged(ConpoundButton buttonview, boolean isChecked) {
Log.i(TAG, "checkz " + isChecked);
1
});
Capitulo 7 I Interface grca - View 199
Button b = (Button) ndViewById(R.id.buttonEnviar);
b.set0nClickListener(new 0nC1ickListener() {
public void onC1ick(View v) {
Log.i(TAG, "0K");
// Compara o id do radioSin
booiean concorda = R.id.radioSim == group.getCheckedRadioButtonId();
boolean receberEmai1 = check.isChecked();
StringBuffer sb = new StringBuffer();
sb.append("Nome: ").append(tetNome.getText())
.append("\nReceber Email: ").append(receberEmai1 ? "Sim" : "No")
.append("\nConcorda: ").append(concorda ? "Sim" : "No");
Toast.makeTet(Exemp1oCheckRadio.this, sb.toString(), Toast.LENGTH_SHORT).show();
}
});
}
NOITIE
__ .,
Concorda?
{ ,,.,i Sim ,; No
Receber Email ?
[ Receber email
Enviar
A seguir, podemos visualizar um exemplo que cria um combo na tela que lista
os nomes dos planetas do sistema solar. Ao selecionar algum plafl, sua foto e
exibida na tela.
i /res/Iayout/activity_eempIo_spinner.xmI
<?ml version="1.0" encoding="utf-8"?>
LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wFD_C"@"t"
android:orientation="vertical">
<TextView
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Selecione una opo" />
<Spinner android:id="@+id/conboPlanetas"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:drawSelector0nTop="true"
android:prompt:"@string/teto_combo" />
<InageView android:id="@+id/img"
android:layout_width="match_parent" android:layout_height="match_parent" />
ExempIoSpinnerActivity.java
public class EemploSpinnerActivity extends Activity {
// Planetas
private int[] imagens = { R.drawable.mercurio, R.drawable.venus, R.drawable.terra,
R.drawable.marte, R.drawable.jupiter, R.drawable.saturno, R.drawable.urano,
R.drawable.netuno, R.drawable.plutao };
private String[] planetas = new String[] { "Mercrio", "Vnus", "Terra", "Marte",
"Jpiter", "Saturno", "Urano", "Netuno", "Pluto"};
@0verride \
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eemplo_spinner);
nal Inageview imagem = (ImageView) ndViewById(R.id.img);
nal Spinner combo = (Spinner) ndViewById(R.id.conboPlanetas);
ArrayAdapter adaptador = new ArrayAdapter<String(this,
android.R.layout.sinple_spinner_iten, planetas);
Captulo 7 I Interface grca - View 201
adaptador.setDropDownViewResource(android.R.layout.sinple_spinner_iten);
combo.setAdapter(adaptador);
// Se selecionar algun planeta atualiza a imagem
combo.set0nItenSelectedListener(new Adapterview.0nItemSelectedListener() {
@0verride
public void onItemSelected(AdapterView<?> parent, View v, int posicao, long id) {
// Atualiza a imagem
imagem.setInageResource(imagens[posicao]);
}
@0verride
public void onNothingSelected(AdapterView<?> parent) { }
});
}
Se for necessrio obter o item selecionado, utilize algum dos seguintes mtodos:
Mtodo Descrio
Object getSelectedItem() Retorna o item selecionado.
long getSelectedIternId() Retorna o id do item selecionado.
int getSelectedItenPosition() Retorna a posio do item selecionado. Essa posio
equivalente ao array fornecido para o spinner.
Nesse exemplo, preferimos monitorar o estado do Spinner em tempo de execuo.
Para isso, implementamos a interface Adapterview.OnItemSelectedListener e o mtodo
onItenSelected(parent,view,posicao,id), com o objetivo de atualizar a imagem auto
maticamente quando um planeta for selecionado. A gura 16 mostra o planeta
Terra selecionado no combo.
, . Terra
Selecione uma opo Selecione uma opo
' Mercuno gf
.
` Vnus
fe -;z . ,. .V W~ . ,um ~-_. 7_,, _.,x __y_ a , I,
$?* : ff?? ,..1 f.. ;.. 513
- f ~=. .~ =,=;:-";,_,..u.;s i. fz z,'.^z..;<1g z, z== ;. _z
zk
i z z f. pq, 4-3;z-
z-V Yzzzz .^z,=>-
;o....: .ez-^_zM=z^~.
:jaz .- zf -~ _,y,.
~ .rj:._fe%*3zf:i.:sfz- ';g;.*?i*z+2'^.r'E; 7:
Marte
,.
Jupiter
1 SBUITK)
Voc pode criar seu prprio recurso XML se desejar customizar a interface,
ou at utilizar outro padro da plataforma com o 0 recurso android.R.layout.
simple_spinner_dropdown_iten. Altere o exemplo para esse cdigo e conra o resultado.
adaptador . setDropDownViewResource( android . R . layout . simple_spinner_droDdW"_t@f'\);
A seguir, podemos ver um exemplo muito simples de como usar a classe Prog ressDialog.
ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle("Eemplo");
dialog.setMessage("Buscando imagem, por aguarde...");
dialog.setIndeterninate(true); // Indica para executar por tempo indeterminado
dialog.setCancelable(true); // Se pode cancelar caso pressione o voltar
dialog.show();
/res/layout/activity__exempIo_progress_diaIog.xmI
<?ml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<ImageView android:id="@+id/img" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="tCenter" />
ExempIoProgressDiaIogActivty.java
public class EemploProgressDialogActivity extends Activity {
private static nal String URL = "http://livroandroid.com.br/imgs/livro_android.png";
private Progressbialog dialog;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eemplo_progress_dialog);
// Abre a janela com a barra de progresso
dialog = ProgressDialog.show(this,"Exemplo",
"Buscando imagem, por favor, aguarde...", false,true);
downloadImagem(URL);
}
}.start();
}
AndrodManifest.xmI
<manifest . . . />
uses-permission android:nane="android.pernission.INTERNET" />
<application ... />
</manifest
., . l V
A
v.
Goo Ie
Aprenda a criar aplicaes para dispositivos mveis 5
com o Android SDK `
ExempIoProgressBarActivity.java
public class EemploProgressBarActivity extends Activity {
private static nal String TAG = "livro";
private ProgressBar mProgress;
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_progressbar);
// Barra de Progresso
nProgress = (ProgressBar) ndViewById(R.ld.barraProgresso);
Button b = (Button) ndViewById(R.id.bt0K);
b.set0nClickListener(new Button.0nClickListener() {
@0verride
public void onClick(View view) {
new Thread(new Runnable() {
public void run() {
for (int i = 0; i <= 100; ++) {
}
l
Captulo 7 I Interface grca - View 207
}});
i rl
}
}
}).start();
nal int progress = i;
// Atualiza a barra de progresso
run0nUiThread(new Runnable() {
});
try {
public void run() {
}
Log.d(TAG,">> Progress: " + progress);
mProgress.setProgress(progress);
Thread.sleep(200);
} catch (InterruptedEception e) {}
Log.d(TAG,"Fim.");
Nesse cdigo, uma thread criada para simular o processamento, e para demo
rar um pouco foi feito um Thread.sleep(200), que coloca a thread para dormir por
ZOO milissegundos a cada iterao. A gura 728 mostra o resultado com a barra
de progresso sendo atualizada. Lembre-se de que, se voc executar o projeto de
exemplo deste captulo no emulador, a barra de progresso ser vermelha, que foi
r
mag;is31zz l r
a cor definida para "accent color" do tema Material.
Progressor Progresslar
1.rV._i
Barra de Progresso Barra de Progresso l
s
4
208 Google Android - 4' edio
7.14 Toast - alertas rpidos
A classe androd.wdget.Toast utilizada para inostrarlcrtas
a S Para o usurio. Cada
alerta pode ser visualizado na tela por um tempo curto, especificado pela constante
Toast.LENGTH_SHORT, ou por um tempo longo se utilizar a constante Toast.LENGTH_LONG.
A forma mais simples de criar um alerta com o m todo Toast .makeTet(conteto,
mensagem, tempo):
Na maioria dos casos, somente o mtodo anteriorj suciente para exibir men
sagens de alerta para o usurio. Entretanto, pode ser que seja necessrio exibir
um alerta com uma interface mais complexa. Para isso, a classe Toast contm o
mtodo setVew(vew), o qual pode ser chamado para congurar a View que ser
exibida no alerta, que pode ser simplesmente uma imagem definida pela classe
Imagevew ou mesmo uma tela com um layout complexo.
Por exemplo, para criar um alerta que no lugar de algum texto exiba uma imagem,
poderamos utilizar o seguinte trecho de cdigo:
Imagevew imagen = new ImageVew(ths);
imagen.setImageResource(R.drawab1e.smile);
Toast toast = new Toast(this);
toast.setVew(magen);
toast.setDuraton(Toast.LENGTH_LONG);
toast.show();
Toggleutton '
t. mw
SpinnerMula: . , Check e Radio
Mensagem Progressialog .
No Sim, ProgressBar
AlertDialog
, . Clicou em Sim!
L|stV|ew -
Nesse caso, o context uma referncia para a classe Activity, ou muitas vezes e o
this. Tambm podemos utilizar 0 seguinte atalho para obter 0 Layoutlnater.
Layoutlnater inate = Layoutlnater.from(context);
7.17 ListView
/res/layout/actvity_exempIo_|istview.xmI
<?xn1 version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:orientation="vertica1" android:padding="16dp" >
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:1ayout_height="0dp"
android:1ayout_weight="1"
android:1ayout_margin="10dp" /
<Vew
android:1ayout_width="natch_parent" android:1ayout_height="z@dp"
android:background="@co1or/primary" /
</LinearLayout
Captulo 7 I Interface grca - View 2"
Quem fornece os dados para preencher o Listview um adapter, que uma classe que
implementa a interface android .widget . ListAdapter. Opcionalmente podemos estender
a classe android.widget.BaseAdapter que j implementa essa interface e deixa poucos
mtodos abstratos para terminarmos a implementao. O cdigo a seguir mostra um
adapter que vai preencher o ListView com uma lista que tem os nomes dos planetas.
Para cada planeta ser criada uma vievig que nesse exemplo um simples Textview.
Veja os comentrios no cdigo para entender o que faz cada mtodo do adapter.
SimpIesAdapter.java
public class SimplesAdapter extends BaseAdapter {
private String[] planetas = new String[] { "Mercrio", "Vnus", "Terra", "Marte",
"Jpiter","Saturno", "Urano", "Netuno", "Pluto"};
private Context context;
public SimplesAdapter(Context context) {
SUDer();
this.contet = context; // O context necessrio para criar a view
}
@0verride
public int getCount() {
return planetas.length; // Retorna a quantidade de items do adapter
}
@0verride
public Object getItem(int position) {
return planetas[position]; // Retorna o objeto para esta posio
}
@0verride
public long getItemId(int position) {
return position; // Retorna o id do objeto para esta posio
}
@0verride
// Retorna a view para esta posio
public View getView(int position, View convertview, ViewGroup parent) {
String planeta = planetas[position];
Textview t = new TextView(context);
oat dip = 56;
oat densidade = context.getResources().getDisplayMetrics().density; // Densidade
// da tela
int px = (int) (dip * densidade + 0.5f);
t.setHeight(px);
t.setTet(planeta);
return t;
}
}
212 Google Android - 4 edio
. _ , - .-, .. zo e definindo a
~ 1 ` ` ado diretamente
-.. ..-.-.-.. - s, conforme
U codigo do adapter esta criando um Textview por Pmffama
. -u 50
altura como 5Odp. Veja no codigo que 0 numero 50 nao L utilll
para definir a altura do Textview, pois isso traria resultados Cllffemff fl
resoluao e densidade da tela do dispositivo. Por isso, essc calculo converte dip
para 5Opx. Mas nem sempre 5Odp 5Opx, pois 0 f5Ulf3d0 da 0nVf5a0 P) C
ser lOOpx, l50px, 200px etc., conforme a densidade da tela. Por isso a conversao e
necessaria. Mas no se preocupe com isso agora, estou apenas alertando sobre um
problema comum. Ns vamos estudar esse assunto em mais detalhes no capitulo
30, sobre como criar aplicativos compatveis com diferentes tamanhos de telas.
Para nalizar o exemplo, segue o cdigo-fonte da activity Para preencher a lista,
preciso chamar o mtodo setAdapter(adapter) do Listview informando o adapter.
A lista ter a quantidade de linhas que o adapter retornar no metodo getCount().
ExempIoListViewActivity.java
public class EenploListViewActivity extends Activity inplements 0nItemClickListener {
protected static nal String TAG = "livro";
private Listview listview;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exenplo_listview);
// Listview
listview = (tistview) ndViewById(R.id.listview);
listView.setAdapter(new SinplesAdapter(this));
listview.set0nItenClickListener(this);
}
public void onItemClick(AdapterView<? parent, View view, int idx, long id) {
// Objeto selecionado, que neste caso era de um array de strings
String s = (String) parent.getAdapter().getItem(idx);
Toast.makeTet(this, "Texto selecionado: " + s + ", posio: " + idx,
Toast.LENGTH_SHORT).show();
}
* 1. I
Parabns! Voc acabou de fazer o primeiro exemplo de ListViev\g um dos compo
.-. .
nentes mais utilizados em aplicativos.
1 I
-.. _ _zll
1 Mefcm
Vnus
Q H... ,la. .l_pop.. r,,, _.-, 1
, gtn,
Jgne-
5 Urano
l
I
/res/layout/adapter_simpIes.xmI
<?xml version="1.0" encoding="utf-8"?>
<LnearLayout xmlns:android="http://schemas.androd.com/apk/res/android"
android:1ayout_width="match_parent" androd:1ayout_heght="wrap_content" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="50dp" />
Feito isso, altere o cdigo do mtodo getVew() para inflar o layout. Depois de fazer
isso, o resultado ser o mesmo de antes, mas desse jeito o cdigo ca bem mais
organizado. Nem sempre o layout do adapter ser to simples; o recomendado
sempre separar a interface em arquivos XML.
214 Google Android - 4 edio
gfu SimpIesAdapter.java
@Override
public View getView(int position, View convertview, VIGWGFOUD Df@t) {
String planeta = planetas[position]; .
View view = Layoutlnater.fron(context).inate(R.layout.adapter_siD1S P3f@t flS);
Textview t = (Textview) view.ndViewById(R.id.tet);
t.setTet(planeta);
return view;
}
/res/Iayout/activity_exempIo_Iistview.xmIz
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout nlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="natch_parent" android:layout_height="match_parent"
android:orientation="vertical" android:padding="16dp"
<ImageView android:src="@drawable/ic_launcher"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:layout_height="Gdp"
android:layout_weight="1"
android:layout_margin="10dp" />
<InageView android:src="@drawable/ic_launcher"
android:layout_width="wrap_content" andro`d:l h
1 ayout_ eight="wrap_content" />
Captulo 7 n Interface grca - View
Para preencher a lista do Listview, vamos criar uma lista de planetas, entao crie a
classe Planeta. Para o exemplo funcionar, copie as guras de cada planeta e 1ns1ra na
pasta /res/drawable. Elas podem ser encontradas no projeto de exemplo deste capitulo
P|aneta.java
public class Planeta {
public String nome;
public int img; // R.drawable.xx
public Planeta(String nome, int img) {
this.nome = nome;
this.img = img;
}
ExempIoListViewActivity.java
public class EemploListViewActivity extends Activity implements 0nItemClickListener {
protected static nal String TAG = "livro";
private Listview listview;
private List planetas;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eemplo_listview);
// Listview
listView = (Listview) ndViewById(R.id.listview);
planetas = Planeta.getPlanetas();
216
}
}. listView.setAdapter(new PlanetaAdapter(this, planetas))
listview.set0nItemClickListener(this);
Google Android - 4 edio
}
Planeta p = this.planetas.get(id);
Toast.nakeTet(this, "Planetaz " + D.0W, TOGS-LENGTH_5H0RT)-5h0W();
PIanetaAdapter.java
public class PlanetaAdapter extends BaseAdapter {
private nal Context context;
private nal List planetas;
public PlanetaAdapter(Context context, List planetas) i
this.contet = context;
this.planetas = planetas;
}
@0verride
public int getCount() {
return planetas != null ? planetas.size() : 0;
1
@0verride
public Object getIten(int position) {
return planetas.get(position);
}
@0verride
public long getItenId(int position) {
return position;
}
@0verride
public View getView(int position, View convertview, ViewGroup parent) {
// Ina a view
View view = Layoutlnater.fron(contet).inate(R.layout.adapter_planeta, parent, false);
// Faz ndViewById das views que precisa atualizar
Textview t = (Textview) view.ndViewById(R.id.tNonePlaneta)
Inageview img = (lnageview) view.ndViewById(R.id.ingPlaneta);
// Atualiza os valores das views
Planeta planeta = planetas.get(position);
t.setTet(planeta.nome);
img.setImageResource(planeta.img);
Captulo 7 nz Interface grca - View
/res/layout/adapter_pIaneta.xmI
<?xml version="1.0" encoding="utf-8"?>
<LnearLayout xmlns:android="http://schemas.androd.com/apk/res/androd"
android:layout_width="natch_parent"
androd:layout_height="?android:attr/listPreferredItemHeight"
android:gravtty="center_vertcal"
android:orientation="horlzontal">
<ImageView android:id="@+id/ingPlaneta"
android:layout_wdth="0dp" android:layout_height="wrap_content"
android:layout_weight="3"
android:src="@drawable/planeta_03_terra" />
<TetVew android:id="@+id/tNomePlaneta"
androd:layout_wdth="0dp" android:layout_hetght="wrap_content"
android:layout_weight="7"
android:layout_marginLeft="10dp" android:textColor="#000G00" />
Dica: no cdigo do layout XML, a altura da view do adapter foi denida como
androd:layout_height="?android:attr/lstPreferredItenHeight". Isso acessa um atributo de
dimenso nativo do Android, que retorna a altura recomendada pela plataforma
para uma linha do ListVieW.
Nota: existe uma classe especial de activity que a ListActivtty, a qual j declara
seu prprio layout com um nico Listview. Mas eu prefiro sempre estender minhas
classes diretamente de activity e adicionar um Listview no layout, pois assim
o layout ca mais exvel e voc tem controle do que est fazendo. Para sua
consulta, a MainActivity do projeto de exemplo deste captulo filha de ListActivity.
218 Google Android - 4 edio
* M
V
" l Marte
z . ..- ~
-, z.i___.T-.
"z~
-z zV z.~- ~i..
_ -..M
Figura ZH - ListView.
7.19 GridVew
/res/Iayout/activity_exempIo_gridview.xmI
<?m1 verson="1.0" encodng="utf-8"?>
<LnearLayout m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:layout_heght="match_parent"
androd:orentation="vertca1" android:paddng="16dp" >
<TetVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:text="Eemp1o de GrdVew:" />
GridView android:d="@+id/grd1"
androd:1ayout_wdth="match_parent" android:layout_heght="match_parent"
androd:paddng="1@dp" androd:gravty="center"
androd:vertca1Spacng="1Gdp" BDFO2h0FZOt81SDCg:"ldp"
android:numCo1umns="auto_t" android:co1umnwidth="40dp" />
</LnearLayout>
Para exibir as imagens no Gridview, necessrio criar um adapter que retorne uma
lista com as imagens necessrias. Sendo assim, foi denido no arquivo XML um
id para o Gridview, para que ele possa ser recuperado no cdigo.
A seguir, temos uma classe de adapter que recebe um array de imagens e cria um
Imageview para cada uma.
ImagemAdapter.java
public class ImagemAdapter extends BaseAdapter {
private Context ctx;
private nal int[] imagens;
public AdaptadorImagem(Context c, int[] imagens) {
this.ctx = c;
this.imagens = imagens;
}
@Override
public int getCount() { return imagens.length; }
@0verride
public Object getItem(int posicao) { return posicao; }
@0verride
public long getItemId(int posicao) { return posicao; }
@Override
public View getView(int posicao, View convertview, ViewGroup parent) {
// Ina a view que est no XML
View view = LayoutInater.from(this.ctx).inate(R.layout.adapter_imagem_gridview,
parent,false);
// Utiliza o ndViewById para obter o Imageview
Imageview img = (ImageView) view.ndViewById(R.id.img);
// Altera a imagem (baseado na posio do array)
img.setImageResource(imagens[posicao]);
// Retorna a view
return view;
}
Para o cdigo compilar, crie este arquivo XML de layout que ser inado pelo
cdigo do adapter. Veja que o nome do arquivo segue a notao adapter_nome.xml.
no Google Android - 4' edio
f /res/layout/adapter_imagem.xml
<?ml version="1.9" encoding="utf-8"?
<LinearLayout mlns:android="http://schemas.android.com/apk/FGS/"dfd" H
android:layout_width="wrap_content" android:layout_hei9ht="wrap-C"te"t >
<ImageView android:id="@+id/img" android:layout_width="wFD_Ct@"t"
android:layout_height="wrap_content" />
Nota: observe que o mtodo getView(posicao,view,parent) deve retornar a View que vai ser
inserida em determinada posio do Gridview. O conceito de adapters (adaptadores)
muito utilizado no Android, e quanto antes voc entender isso melhor. Note que
para criar a view foi inflado um layout XML com ajuda da classe Layoutlnater.
Para nalizar o exemplo, este o cdigo da activity que vai preencher o Gridview
com um array de imagens. A classe ImagemAdapter deve retornar a quantidade de
imagens que precisam ser adicionadas no Gridview.
ExempIoGridViewActvity.java
public class EemploGridViewActivity extends Activity {
// Array com os ids das imagens
private int[] imagens = { R.drawable.smile1, R.drawable.smile2,
R.drawable.smile1, R.drawable.smile2, R.drawable.smile1,
R.drawable.smile2, R.drawable.smile1, R.drawable.smile2 };
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_eemplo_gridview);
Gridview grid = (Gridview) ndViewById(R.id.grid1);
grid.set0nItemClickListener(onGridViewItemClick());
// Informar o adapter para preencher o Gridview
grid.setAdapter(new ImagemAdapter(this, imagens));
}
// Evento ao clicar no item do grid
private 0nItemClickListener onGridViewItemClick() {
return new 0nItemClickListener() {
public void onItemClick(AdapterView<? pargnt, View V, int poScao,10ng id) {
_ Y IS, magem selecionada: " +
Toast.makeText(EemploGridViewActivit .th` "I
posicao, Toast.LENGTH_SHORT).show();
}
};
}
}
Captulo 7 I Interface grca - View 221
Observe que o mtodo set0nItemC1ckLstener(lstener) da classe Grdvew pode ser
utilizado para tratar os eventos gerados caso o usurio selecione e pressione al
guma imagem. No mtodo onItemC1ck(parent,vew,poscao,d), possvel recuperar
qual imagem foi selecionada. A gura 112 exibe o resultado desse exemplo.
G
00 0
Exemplo de GridView:
Nota: durante o livro, vamos estudar outros exemplos sobre os adapters. Se voc
perceber, uma simples classe que deve implementar o mtodo getCount() para
informar quantas views existem, e depois o mtodo getVew() chamado N vezes
para criar cada view. Seja para criar um grid com Grdvew ou uma lista com Lstvew
, os adapters so gurinhas carimbadas no desenvolvimento para Android e so
responsveis por fornecer o contedo e preencher esses componentes.
7.20 Gallery
Sabe quando voc abre o lbum de fotos no Android e faz o gesto de swipe (deslizar)
para os lados para ver as fotos? A classe androd.wdget.GaU.ery faz justamente isso.
Nesse exemplo, criaremos uma galeria de imagens com as fotos de alguns planetas.
/res/Iayout/activity_exempIo_gaIIery.xm|
<?xml verson="1.0" encodng="utf-8"?>
<LnearLayout m1ns:android="http://schemas.androd.com/apk/res/androd"
222 Google Android - 4 edio
android:layout_width="match_parent" android:layout_hei9ht="match-parentH
android:orientation="vertical" >
<TetView
android:layout_width="wrap_content"androidzlayout _h 01 9ht="wrap_content"
android:text="Eemplo de Gallery" android:gravity="centF" />
<Gallery android:id="@+id/gallery"
android:layout_width="match_parent" androidzlayout _h 6i ht="match_parent"
9
android:gravity="center" />
A forma de usar o Gallery idntica do Gridview. Este prximo exemplo cria uma
galeria de fotos a partir de um array de imagens.
ExempIoGaIIeryActivty.java
public class ExemploGalleryActivity extends Activity {
// Planetas
private int[] imagens = { R.drawable.mercurio, R.drawable.venus,
R.drawable.terra, R.drawable.marte, R.drawable.jupiter,
R.drawable.saturno, R.drawable.urano, R.drawable.netuno,
R.drawable.plutao };
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_gallery);
Gallery g = (Gallery) ndViewById(R.id.gallery);
g.setAdapter(new ImagemAdapter(this, imagens));
9.set0nItemClickListener(onGalleryItemClick(this));
}
};
}
}
Captulo 7 I Interface grca - View
Observe que, para deixar o exemplo mais completo, o mtodo onItemC1ick( parent
v1ew,poso,d) foi implementado para recuperar a posio da imagem selecio
nada. Nesse caso, a classe Toast foi utilizada para exibir um alerta na tela, que na
verdade desenhado pelo Imagevew da imagem selecionada.
A gura 113 mostra a galeria de fotos com o planeta Terra selecionado. Para testar
os exemplos, navegue na galeria fazendo o gesto de swipe para os lados.
Lxemplo de Gallery
7 21 ViewPager
A classe androd . support.v4.vew.VewPager faz parte da biblioteca de compatibilidade
No Android Studio, a biblioteca de compatibilidade configurada no arquivo
app/buildgradle.
Lembrando que a biblioteca de compatibilidade baixada pelo SDK Manager pelo
item Android Support Library e recomenda~se sempre mant-la atualizada. Depois de
224 Google Android - 4 edio
. . o as c . - - f ~ ~ / d `d_sdk/ext
baixar o item Android Support library, a biblioteca sera instalada em an~ roz biblioterzzz/
android/support. L, voce vai encontrar as pastas v4 v7 C v13 que Sa 714 HS
dc compatibilidade com cada API Level, conforme mostra a gura .
i 5. 4
\*\(l'll*(l l<1`vv A
pc fi Il i C instiiieti
/\Yii`lf(`iil 4-' (`\l l l
L1p.
1T1lfl
l install llcl
I _ '1l!`il
l lv Q i il <)f`l(l''l('l l 'll fl
l
l
l
V1 `
~ ' woiict zw
hi
\ bixo-x!~af\ V I I 1 b L' Rigrpitg. H;
\p\[( \,_. 3 \.i\.*"t"
l 5 `^.i(\~.l lt?
app/buiId.grad|e
apply plugin: 'con.android.appllcation'
dependencias {
conpile leTree(dir: 'libs', include: ['*.jar']) // Adiciona arquivos .jars da pasta
I/ libs como dependncia
Captulo 7 I Interface grca - View 225
// Dependncia da biblioteca de compatibilidade v4
compile "com.android.supportzsupport-v4:21+"
l
/res/layout/activity_exempIo_view_pager.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >
<TetView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:tet="Eemplo de ViewPager" />
<android.support.v4.view.ViewPager android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_height="wrap_content" />
|magemPagerAdapter.java
public class AdaptadorImagem_ViewPager extends PagerAdapter {
private Context ctx;
private nal int[] imagens;
public ImagemPagerAdapter(Context c, int[] imagens) {
this.ct = c;
this.imagens = imagens;
}
@0verride
public int getCount() { // Quantidade de views do adapter
return imagens != null ? imagens.length : 0;
l
226 Google Android - 4 edio
@Override _ _
// Ina a view ,
public Object instantiateltem(ViewGroup container, int D51t1") {
@Override _
}
return view;
@0verride
public boolean isViewFromObject(View view, Object object) {
// Determina se a view informada igual ao object retornado pelo instantiateltem
return view == object;
}
cada mtodo:
Mtodo ` Descriao p
Para auxiliar o entendimento do cdigo-fonte dessa classe, segue a explicao de
@Override
public void onPageScrolled(int position, oat position0ffset,
int position0ffsetPiels) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
Para ter uma ideia do que estou falando, a gura Z15 mostra o resultado ao utilizar
'ile1
O ViewPager com o PagerTt1eStrp.
.
Rf,'lj}`~* :II \v.Ic":f\ zT `
.. .- M -` .... _
..._j
f'~.,~.v,@_ 3`_Qg~;_.. _. .!Z<._\_;>'_f. > sz _,w ,, -,.;; z. j
/res/layout/activity_exempIo_view_pager__tab_strip.xmI
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout . . . >
<android.support.v4.view.ViewPager android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_height="wrap_content" >
<android.support.v4.view.PagerTabStrip android:id="@+id/viewPagerTabStrip"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_gravity="top" android:background="#33b5e5"
android:textColor="#fff" />
Para mostrar o ttulo acima do ViewPager, preciso que a classe do adapter imple
mente o mtodo getPageTitle(int page) que deve retornar o ttulo da pgina. Nesse
exemplo, vamos usar novamente a classe Planeta, a qual tem o nome do planeta
e o inteiro com o recurso da imagem. A classe do adapter recebe uma lista de
planetas e retorna a view que contm a foto do planeta, tambm retorna o nome
do planeta no mtodo getPageTitle(int page).
PlanetasPagerAdapter.java
public class PlanetasPagerAdapter extends PagerAdapter {
private Context ctx;
private nal List planetas;
public PlanetasPagerAdapter(Context c, List planetas) {
// Para o cdigo completo veja nos exemplos do livro. Esse um adapter simples.
// O importante o mtodo getPageTitle(page) retornar o titulo.
@0verride
public CharSequence getPageTitle(int page) {
// Titulo para mostrar no PagerTitleStrip ou PagerTabStrip
Planeta planeta = planetas.get(page);
return planeta.nome;
}
}
230 Google Android - 4' edio
, . . . - -. _ . . . a ~ ` a acia
O codigo da activity somente cria a lista dc planetas c configura ptcr no
ViewPager. O resto tudo automatico, c o ttulo retornado pelo adapter sera mos
trado na tah do viewPager.
i'=fi MainActivity.java
Dlca: para alterar por programao a pgina que o ViewPager est mostrando,
utilize o metodo setCurrentIten(int page).
7.23 lmageSwitcher
Outra classe bastante til a android.widget.ImageSwitcher, utilizada para mostrar
uma imagem aps outra de forma animada.
) /res/Iayout/activity_eempIo_image_switcher.mI
<?nl version="1.0" encoding="utf-8"?>
LinearLayout nlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="natch_parent"
android:orientation="vertical" android:padding="10dp" >
Button android:id="@+id/btProina"
});
imageSwitcher.setInAnimation(Animationtils.loadAnimation(this,android.R.anim.fade_in));
imageSwitcher.set0utAnimation(Animationtils.loadAnimation(this,android.R.anim.fade_out));
View btProima = ndViewById(R.id.btProima);
btProxima.setOnClickListener(new 0nClickListener() {
@0verride
public void onClick(View arg0) {
if(idx == imagens.length) { idx = 0; }
imageSwitcher.setImageResource(imagens[idx++]);
}
});
}
PXME
7.24 WebView
Se por algum motivo voc precisar exibir uma pgina web dentro do aplicativo. a
classe androd .webktwebvew pode ser til. O funcionamento desse componente iden
tico ao browser do Android, isso porque internamente utilizada a engine \VebKit.
Essa e uma das views mais utilizadas nos aplicativos, principalmente pelos adeptos
da criao de aplicativos hbridos que utilizam HTML5 e JavaScript para criar a
interface grca. Primeiramente, para ter acesso a internet, declare a permisso
INTERNET no arquivo AndridMani>st..\m1. Quando o usurio baixar o aplicativo dd
Google Play; sero mostradas para ele todas as permisses que o aplicativo precisa
utilizar, e baseado nisso o usuario pode aprovar ou no a instalaao.
AndroidManifest.xmI
<manfest . . . />
uses-permission android:name="android.permission.INTERNET" /
<applcaton ... />
</manfest>
Para demonstrar 001110 exibir uma pgina de internet usando o Nebvew, vamo
criar um simples exemplo que exibe a pgina www_1W(,a,,dm(_mm_,,_
Captulo 7 I Interface grca - View 233
/res/Iayout/activity_exempIo_webview.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xnlns:android=http://schemas.android.com/apk/res/android" ... />
<FraneLayout android:layout_width="match_parent" android:layout_height="match_parent" >
<webView android:id="@+id/webview" android:layout_nargin="10dp"
android:layout_width="natch_parent" android:layout_height="match_parent" />
ProgressBar android:id="@+id/progress" android:layout_gravity="center"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
ExempIoWebViewActivity.java
public class EemplowebViewActivity extends Activity {
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eemplo_webview);
nal Nebview webview = (webview) ndViewById(R.id.webView);
nal View progress = ndViewById(R.id.progress);
progress.setVisibility(View.INVISIBLE);
webview.loadUrl("http://www.livroandroid.com.br");
webview.setwebViewClient(new webViewClient(){
@Override
public void onPageStarted(webView view, String url, Bitmap favicon) {
progress.setVisibility(View.VISIBLE); // pgina comeou a ser carregada
}
@Override
public void onPageFinished(webView view, String url) {
progress.setVisibility(View.INVISIBLE); // pgina foi carregada
}
@Override
public void onReceivedError(webView view, int errorCode,String description,
String failingUrl) {
// Erro ao carregar a pgina do webview (endereo errado, ou erro de conexo)
}
});
}
A gura Z17 mostra a pgina do site do livro aberta dentro do webview. Veja que
utilizamos o FrameLayout para inserir o ProgressBar por cima do webview. Enquanto
a pgina est sendo carregada, o ProgressBar ca visvel para mostrar a animao.
234 Google Android - 4 edio
l wmpiu de Wvbvrrw """l'k"1"W"hV""'
Google \
di o Android 3
E nbqoig
livro campeao em vendas e
i no Android
l mcgmondado para iniciantes
jTpau, qugm deseja aprender
concertos principais doos
desenvolvimento para Andr0d
Explica do bsnco ao avanado.
confira o sumalio
Nota: lembre-se de que necess rio declarar a permisso INTERNET para o WebView
funcionar.
E5525 tcnicas de injetar cdigo HTML ejavaScript no Nebvew esto fazendo a festa
dos adeptos de HTML5 e aplicativos hbridos, e com base nelas que populares
frameworks como o PhoneGap funcionam. Esse tipo de abordagem permite que
desenvolvedores com vasta experiencia em web entrem no mercado dos aplicativos
IUVS, C UfiliZ1n cdigo HTML para criar a interface grca
1e,_.
Mas no estou aqui para discutir sob re este assunto nativo vs. HTML. O objetivo
deste livro ensinar a criar aplicativos nativos para Android alm de eitplieir o
principais conceitos da plataforma.
Captulo 7 I Interface grca - View 235
7.25 Movimentando uma imagem pela tela com touch
Neste prximo exemplo, vamos movimentar uma imagem pela tela, utilizando
touch screen. No emulador voc poder utilizar o mouse para movimentar a ima
gem e naturalmente em um celular real voc vai arrastar a imagem com o dedo.
A classe View contm o mtodo onTouchEvent(MotionEvent), que sempre chamado
quando um toque na tela realizado. Como parmetro temos um objeto do tipo
MotionEvent, com o qual possvel recuperar as posies x e y do toque.
public boolean onTouchEvent(MotionEvent event) {
oat x = event.getX();
oat y = event.getY();
return true;
}
Esse mtodo deve retornar true caso a view tenha tratado o evento, ou false se
para delegar a tarefa para as outras views da tela. Se nenhuma view tratar o
evento, o mesmo mtodo ser chamado na activity responsvel pela tela. A seguir
podemos ver um exemplo completo que permite mover a imagem do boneco do
Android pela tela. A gura foi inserida em /res/drawable/android.png.
TouchScreenView.java
public class TouchScreenView extends View {
private static nal String TAG = "livro";
private Drawable img;
int x, y;
private boolean selecionou;
private int larguraTela;
private int alturaTela;
private int larguralmg;
private int alturalmg;
public TouchScreenView(Context context) {
super(context, null);
// Recupera a Imagem
img = context.getResources().getDrawable(R.drawable.android);
// Recupera a largura e altura da imagem
larguralmg = img.getIntrinsicwidth();
alturalmg = img.getIntrinsicHeight();
// Congura a View para receber foco e tratar eventos de teclado
setFocusable(true);
}
@0verride
Google Android - 4 edio
@0verride
// Desenha a tela
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Fundo branco
Paint pincel = new Paint();
pincel.setColor(Color.wHITE);
canvas.drawRect(0, 0, larguraTela, alturaTela, pincel)
// Dene os limites/rea para desenhar
img.setBounds(x, y, + larguralmg, y + alturalmg);
// Desenha a imagem
img.draw(canvas);
}
@0verride
// Move a imagem
public boolean onTouchEvent(MotionEvent event) {
oat = event.getX();
oat y = event.getY();
Log.i(TAG, "onTouchEvent: x/y > " + x + "/" + y);
switch (event.getAction()) {
case MotionEvent.ACTION_DONN:
// Inicia o movimento se pressionou a imagem
selecionou = img.copyBounds().contains((int) x, (int) y);
break;
case MotionEvent.ACTION_MOVE:
// Arrasta o boneco
if (selecionou) {
thS = (t) X - (larguralmg / 2);
thS-Y = (t) y - (alturalmg / 2);
}
break;
case MotionEvent.ACTION_UP:
// Finaliza o movimento
selecionou = false;
Captulo 7 n Interface grca - View 237
break;
}
Feito isso, crie uma activity e congure esta view no mtodo setContentView(view):
TouchScreenViewActivity.java
public class TouchScreenViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TouchScreenView(this));
}
e cz 1
Mova o objeto com o touch
l
_l
s criar nossa r 1
O Android tem varias classes prontas para desenhar compm Cada
uma dessas classes e uma subclasse de V1.ew.Scra que podLm0 P Pfld
classe-lha de View? A resposta sim.
MinhaView.java
package br.com.livroandroid.cap07_view.canvas
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class Minhaview extends View {
// Para denir a cor RGB
private Paint pincelvermelho;
private Paint pincelPreto;
private Paint pincelAzul;
public MinhaView(Context context) {
this(context,null);
}
@0verride
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Desenha um quadrado
canvas.drawRect(20, 20, 200, 200, pincelAzul);
// Desenha uma linha
canvas.drawLine(200, 200, 400, 400, pincelPreto);
// Desenha um circulo
canvas.drawCircle(400, 400, 100,pincelVermelho);
}
_ _' ~'
te' a voce. acnv' ~
A gura 7.19 mostra a pr-visualizao dessa tela pelo ll ._ d t I 'I I
componente desenhado manualmente. Como a pre visua li; 1 / J 1 u o
suficiente para entender o exemplo, nao criaremos aqui nen uma^ ir;
que utilize esse arquivo de layout. Isso ca como exercicio par
No entanto, esse cdigo que desenha o quadrado tem um problema, pois esta
usando valores xos em pixels. Isso vai trazer resultados diferentes em telas qu
apresentem diferentes resolues e densidade. O correto seria utilizar valores em
dp (density independent pixel) e converter o valor para pixel utilizando a den
sidade de cada tela. Por exemplo, 100px pode ser convertido para 75px, 100px.
15Opx, 200px, 300px, conforme a densidade da tela do aparelho. Essas converse
PfC5am SCF feiras HO COdig0 para garantir que o resultado ser o mesmo ind
pendentemente da resoluo e densidade da tela.
Acredito que talvez seja cedo para explicar por que isso necessrio. Prero
continuar o livro com conceitos simples e focar no que voc vai utilizar no dia a
dia. Ainda temos alguns captulos essenciais pela frente e logo vamos comear o
desenvolvimento do aplicativo dos carros.
A metodologia do livro explicar cada conceito de uma vez. Quero focar nos
conceitos principais, aqueles de que, tenho certeza, voc vai precisar no dia a dia.
Conheo muitos desenvolvedores que fazem aplicativos e no sabem explicar o
que dp (density independent pixel), por isso creio que esse assunto possa car
para depois. Para mais detalhes, leia o captulo 30, sobre como suportar diferentes
tamanhos de telas.
f . CAPTULO
Fragments
\_`_
`
A gura 8.1 compara o modelo tradicional de uma aplicao do tipo lista e dl'
lhes, executando no smartphone Qu tablet N o smartphone, duas telas precisam
ser utilizadas para fazer a navegao da lista para a tela de detalhes No tablet
podemos utilizar uma nica tela, aproveitando ao mximo o espag disponvel.
242
. * si 5
(aPtuIo 8 n Fragments 243
_jp.rAA
^V"Y ^ Avify B Activity A mm aos fragmems
Figura 8.1 - Fragment que divide a tela em pedaos.
Dica: se quiser ver um aplicativo com duas telas de lista e detalhes, abra o projeto
Planetas-Activity no Android Studio. Neste projeto, temos uma lista de planetas
e ao clicar na lista o planeta mostrado em outra_tela. Durante este captulo,
vamos trabalhar em cima deste exemplo.
Nesta gura podemos ver que a aplicao para smartphone utiliza a Activity A
e a Activity B como de costume. Na aplicao para tablet, como existe somente
uma tela, ou seja, apenas uma activity dentro dela o contedo separado em dois
fragments. Basicamente um fragment um componente que pode ser inserido
dentro da activity e esse componente ca responsvel por:
1. Criar a view para preencher determinado espao, alm de controlar o seu
estado e tratar os eventos.
2. Pela lgica de negcios para buscar os dados de um web service ou banco
de dados.
Nota: talvez este captulo seja um pouco avanado neste momento, tudo depende
do seu perl. O fato que antes de comearmos a desenvolver o aplicativo dos
carros euaproveito
assunto 7
preciso explicar o que alguns
para abordar um Fragment. E como
topicos mais vou ter de
avanados, explicr
como ta etso
e gerenciamento de estado do fragment. Mesmo que o assunto seja um pouco
avanado continue estudando, pois o livro servira de consulta mais tarde.
7
244 Google Android - 4 edio
8.2 Fragments muito mais do que dividir a tela em duas partS
lista explicao de duas activities no smartphone C llP"5 Ulflil 1lL`UVll)' corn
dois lragments no tablet e classica. inclusive a documentacao ohctal do Androitl
utiliza este exemplo. Mas isso mttitas vezes confunde quem esta ilPl`L`ll(iL`ll\ii>. e
alguns desenvolvedores acham que liragments servem para dividir a teia em duas
partes nos tablets.
Na verdade, os lragments sao componentes que ficam espalhados na tela. sendo
qtte um de setis principais objetivos e reutilizar a logica entre a versao 1i.}Til"ii\I`;
e tablet. Para comearmos bem, vamos acabar com o mito de que um if' fiflift
serve para dividir a tela do tablet em duas partes. A ligura 8.2 mostra algo z_litt-tarte.
a tela do tablet dividida em tres partes.
:rf
{' l,ivtoAnlrt-id
imgnwnt' Tivxt 2
Frqmentl TQIIO 1.
Frqmontl Tutu 3.
if' sz-i~*'
:guru i em tres ttrres rom rttgrnenls.
t z ttn tt_v ivididti
lisse tipo de tela e bem connnn nos tablets, pois assim voc consegue mostrar
varias iniormat`es ao mesmo tempo para o usuario, /\ principal razo de quebrar
a tela em pedaos e para simplihcar o codigo da activity, pois podemos dizer que
cada tragment e responsavel por determinada parte do layout.
A figura 83 mostra um dos meus primeiros projetos para tablets, qurmdti Qgmdei
lragments pela primeira vez. Neste aplicativo, cada pedao da tela e um fragment.
1 .' \ \_' .. t \-~, , ',_ `f .:. . _ _ _
cmo a lista de indices, lista de notcias, lista de videos. area central. grtilice
etc ( s beneltuos dessa organizaeao sao imensos, pois os lragments deixam tt
Captulo 8 n Fragments
, pois e a no f
o Lstview de forma independente do resto da tel a. No nal, o cdigo da activity
fica bastante reduzido ou talvez vazio ` 1 8
de1ga do aos fragments. az nada, e todo o trabalho e
,,
f,ml-...-_.zz
,,:*~Ef- 2 ff yr,
Ponrusus
A TUTORIAL
saem:
wasrts j
3M&FBO'E.
A gura 8.4 mostra outro aplicativo para tablet, em que mais uma vez este conceito
de dividir a tela em partes foi utilizado. Cada parte um fragment, e novamente
isso ajudou a separar as responsabilidades e organizar o cdigo. Se um aplicati
vo desse tamanho fosse criado apenas 'com uma activity o XML de layout seria
imenso e o cdigo-fonte para gerenciar toda a lgica tambm. No entanto, com
fragments a activity apenas divide o seu layout em partes e insere cada fragment
no seu devido espao. Cada fragment por sua vez vai criar a view e gerenciar o
contedo. Simples, no ?
Trabalhar com fragments, porm, muito mais do que apenas separar a tela em
pedaos, pois um dos principais objetivos da API criar um componente de c
digo reutilizvel. Afigura
8.5 mostra uma aplicao compatvel com smartphones
d d afiaura
e tablets. A parte esquer g mostra a verso para tablets. Veja que o
tablet est mostrando a lista de ndices ein determinado local da tela. E na parte
da direita podemos ver a mesmaaplicao no smartphone, corn a mesma lista
h (iooqlv Android 4-'1111W
_\, \ \ _ \ . . 1..1 1 114 1.11.
1
, 1 1 .. . 1 .`\.1..1~1.
1 1 ._
11~111111111~ l'~l1' 1' 11111 1~\1'1111 '111 1111 \I1I 11 11~. 11.11 HH '111 ~' UI 1 ""~11
11
\ "\ 1 `\ \|(. `\( "
111111111111111111' 11-111111 .1\111~1\1.111|1111111|11.1111.1111111111`.11111 -HU 1 * 111111
111\1
1 111111 1 |`1l 1 1111 1 11 111111|11~ 1111 \\1~11~1~1\111 11111111-1'|'|l "1`1'1
1
1
1111 111 1\111111 11 11\ 111111111111 |111~1 1~..1 1111111111111 .11 11 1.l\1'111 |\.l|-1 1 111111111 1111 111
1
V" ` ' V 7 V N
1|n,. '
E1' kd 1
111.11 11111111111 1111 1.11111'l.
nu-zum-.~un.~..,..
^1`
______ _ _ ___ ___- --f_ ; ~ ~-_ ::f1;_^^
otl
' ` ,1zz1z
1 n-. :O
1;-:.\.fz
A Mvwuz..
>|11.11
u.u-4.1.4 14.z~-' -1 .5
PM (em H 1muu11u111\|- 111\M1 111111111 "\.'|l'\ 1111 _1gu~v- 1111 Riu ,mmu
lap 13
I\0\11~1 "I '\ 1 l 1 *" *' 7`<\ ` ` `\,\1; 1
x ` ` ' 1 1\\I\\\ \ ....
.nu - ' .`,,,,___,,,_,,,_,.. ,_ ,,._ _. _ _ _ ,.;,.1,__,. M`
7 h1num1 `
1 11.
A Mmh.
_11.1 \ h.
` ` 1.. \n:;\`..=Q1.
v 'up ,,_1._g,1,_ __, _ .J. ;
\l.J1I4y|J1 qm
1m11v~1oJ~ 1o111~ . ,_ ,, . . . .
NI11|n1|111
^ = ' "' "" ""*"1- 1 ' *' `V , pru
_ . ~ 11
1
:r-:~:_~ ~'~' ' Y-~fl\~_~^_~ ' _ _ _ _ :_ 7 _ _____v__
11111114
NUM:-u
'OHMm1\u
0.=q141w x4s111.
vuguw rw-.14|1\vu 111!
x mqu||4h1N\\vJ1 I1
1114 11.1 zmkmu O 11 1 Vnw u1.zz1vu 1 V'
_ ' ''"'
"'"
1ih\ll
um Bmw Jtanvoiudia ` 1-,
10.1 qzuu dr|||\wn\11 nv
.,,,
mniy npnu
r1qyn\u _, 41h-quo:
. _ 1nUudumm!-cilcul ' 11- ~
1- 1. AbAvzwuL\u
1hm.\h&-1
new
In--up U1 um k11donbc1| vou hn pano 00"'~*
111- 1 uvunwu anti 1'' L ml Hbaciu.
I uR\\$"M
11g11.11\.-1 1)11'111111111.!1'1.11'111111\1;1111'n1
`|
\ ` `\ \ ' 1 \ 1 \\
1
fl _
tudo de determ inado local do layout. Ao selecionar uma tab, basicamente um
9 ._
fragment substitudo por outro,
za W Q.
` ( z
Aws._ ~. z z- O* ~aeza
ll- iii!P P _ ` z~
CTERORS HOM roppggg mpmes WES ~
w
waities GAMES _ Io
i M *I afff*
i?zumz=@s
,_ ,HH,
Hacks ieferemce , _ _
1 ECE _
' ws.. L.
Hom "f'&...f...'; ;1-._ ,f
- ~ - _ ` _ _ _ ._ _ . . __ _ ,m ,a;z,,.z mp mn wma 7 PN0 TOP Fm rovoaossmc mv NEWPMI
Crzmfcs
., _, ` _ ~ . zw* Aplicativos Sociais 2 What Asp M g
Euction
***** -mw .nn _, _
.....
e r wtndfi. ._
Elhmmm T F! commendedfo You 5 F b < vi g
A figura 8.7 mostra o padro de navegao Navigation Drawer, muito utilizado nos apli
cativos modernos. S para dar uma ideia, o Gmail eYouTube utilizam esse padro,
que conhecido popularmente por menu lateral' Tal padro de navegao, da
mesma forma que as tabs, representam a navegao top-level do aplicativo com as
sees mais comuns. Nesse caso existe apenas uma activity pois, ao abrir o menu
lateral e selecionar uma opo, apenas o contedo central alterado, sem fazer a
navegao de telas. Na prtica, ao selecionar uma opo do menu, um fragment
substitudo por outro.
A gura 8.8 mostra outro caso no qual podemos utilizar um fragment, para
solucionar o problema de i nserir o banner na parte inferior da tela. Como esse
e ser inserido em todas as telas, o ideal que ele
banner um componente e dev
-lo no layout. O fragment sozinho vai criar a
seja um fragment, pois basta inseri
view e buscar o contedo necessrio para desenhar O b21Y1nf
Google Android -- 4 edio
zh
uu- """"" 1 n
"' `,:.
i
.z.2., "*"i**^~>.
Q 0(iVa' 1
O nV Q 4
|(\V n H --
._ vf
' #'=_ .
..$;.
a:cs~.;\;I ~ ._
h \F 'm
~. \1_lll\5 xnnplm um qu.u m1cnm5 uulwdl. A \l)I kh.
~ . .1 .\@\\*|'l. SIl\II`l|)hUHL' uu p.n.\
K ' 'm` sc ;\ w;~;
\ H \ m`m'/" h!
` ` ` ( , L` | ~
lulull/..n'u111px\c1m~. /\lllLlIIl\L'I\lL` |1\cs1\\s\z(,ux ,. W um] H ix l 1 U M
. Hk`UIH\'Il\l.l l -n ' ~ ' ~ ~' z z .
` '|I"' ~\1:~l.1 IL`Il\'Il)' cm um I`;\gl1\L`I\l' ;\ssim\ W
um Im vmc )I`k`\`I8.lI` r ~.\ w' V- - .~. o . . ..
* I 'U\Lll.\| x asa u|\1p|\c|1lc,nd<L~- |UmU_
Captulo 8 I Fragments 2 49
8.3 API de Fragments
android.app.Fragment
android.app.FragmentManager
android.app.FragmentTransaction
app/buiId.gradIe
dependencies {
.. - inavas.
Feito isso, podemos utilizar as classes da biblioteca de suporte que cam HO
ara manter a compatibilidade com verses anteriores,
pacote android.support.v4. P
recomenda-se utilizar as seguintes Cl21SSS, U0 lugar das
~ android.support.v4.app.FF9|@t
android.support.v4.app.FF9F\@tMaa9er
android.support.v4.DP-Ffa9etTra"SaCton
250 Google Android - 4' edio
. .__....de,.z
A hihlioteca
-_-._z,, ,..z
compatibilidade
`na
e dtstribuida
ere
existem odere~
pelo SDK M3 8 v `
.P Wber
atualizaoes, por isso recomendo que vote sempre verif-lue se HSOS
novas da hihlioteca.
androd.support.v4.app.Fragmentllanager fm = getSupportFragmentHanager();
MainActivity.java
public class MainActivity extends d '
@0Verrde an r1d-SUPPOF-V4-pp.FragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity nain)~
// 0 FragnentManager nececessrio para brincar com os fragnents
} android'supprtV4'app'Fragmentaaef fm = getSupportFragnentManaqer()~
}
dependencies {
conpile "con.android.support:support-v4:21+"
}
Fragment1.java
public class Fragmentl extends android.support.v4.app.Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedlnstancetate) {
View view = inater.inate(R.layout.fragnent_1, container, false);
// O fragnent livre para ter qualquer lgica aqui
return view;
}
/res/layout/fragment_1.xml
<?ml version="1.0"
tf-8"?> encoding="U _ N
<LinearLayout xmlns:android="hD// _HH
schenas.android.con/apk/res/android
/res/layout/activity_main.xml
<LnearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
mlns:too1s="http://schemas.androd.com/tools"
androd:1ayout_width="match_parent" android:1ayout_heght="match_parent"
fragment android:id="@+d/fragi"
android:1ayout_width="match_parent" android:1ayout_heght="match_parent"
c1ass="br.con.Iivroandrod.cap09_he11ofragnents.Fragment1"
tools:1ayout="@1ayout/fragment_1" />
</LnearLayout>
HGU W Id? g
Pronto! Se voc executar o projeto no emulador, deve ver a mensagem Hello World
Fragment na tela. Para fechar esse tpico, veja que o fragment tem um identificador
que foi declarado no layout.
<fragment androd:id="@+d/fragl" ...
Isso signica que em qualquer local do cdigo podemos recuperar esse fragment
com o mtodo fndFragmentById(d) da classe androd.support.v4.app.FragmentManager. O
cdigo a seguir demonstra como encontrar um fragment pelo id.
ManActvity.java
public class MainActtvty extends androd.support.v4.app.FragmentActivty {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVew(R.layout.activty_ma);
F ntMana er();
androd.support.v4.app.Fragmentanager fm = 9f5UPPf"`t "a9' 9
Fr3gment1 frag1 = (Fragmenti) fm.ndFragmentById(R.td.frag1);
f rag1 ..vocePodeChama r0MetodoQuePrecsarA<IU( )5
}
}
254 Google Android - 4' edio
.. ,_.ultimo
komo _. , __exemplo,v:1mos
'vz]
r1APl ara adicionaru
Qdcmonstrarcomo
za do utiliza
la ouPm
lragment dinamicamente no layout. Nesse caso, fCm0 * g Y . . . ~ _ . radoa se uir.
da activity e dcixc o layout vazio, conforme demonst z 8
t /res/layout/activity_main.xml
<FrameLayout minszznr0i="hiipz//szhemz.anroi.com/pk/res/dfd"
mlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" androidzlayou _ 619
android:id="@+id/layoutFrag">
</span></span> <span class='ocr_word' id='word_1_80' title="bbox 804 1149 996 1200"><span class='xocr_word' id='xword_1_80' title="x_wconf -3">Fragment</span></span> <span class='ocr_word' id='word_1_81' title="bbox 1027 1147 1120 1190"><span class='xocr_word' id='xword_1_81' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_82' title="bbox 1150 1144 1344 1189"><span class='xocr_word' id='xword_1_82' title="x_wconf -1">inserido</span></span> <span class='ocr_word' id='word_1_83' title="bbox 1374 1144 1468 1196"><span class='xocr_word' id='xword_1_83' title="x_wconf -1">aqui</span></span> <span class='ocr_word' id='word_1_84' title="bbox 1498 1154 1544 1185"><span class='xocr_word' id='xword_1_84' title="x_wconf 0">no</span></span> <span class='ocr_word' id='word_1_85' title="bbox 1573 1141 1718 1194"><span class='xocr_word' id='xword_1_85' title="x_wconf 0">layout</span></span> <span class='ocr_word' id='word_1_86' title="bbox 1751 1125 2170 1188"><span class='xocr_word' id='xword_1_86' title="x_wconf -8">"@+d/YOUFFHQ"</span></span> <span class='ocr_word' id='word_1_87' title="bbox 2235 1137 2304 1161"><span class='xocr_word' id='xword_1_87' title="x_wconf -5">"></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_14' title="bbox 590 1227 934 1283"><span class='ocr_word' id='word_1_88' title="bbox 590 1227 934 1283"><span class='xocr_word' id='xword_1_88' title="x_wconf -3"></FraneLayout></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_7' title="bbox 1900 948 2479 1008">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_15' title="bbox 1900 948 2479 1008"><span class='ocr_word' id='word_1_89' title="bbox 1900 969 1918 1008"><span class='xocr_word' id='xword_1_89' title="x_wconf -6">t</span></span> <span class='ocr_word' id='word_1_90' title="bbox 1950 962 1969 1006"><span class='xocr_word' id='xword_1_90' title="x_wconf -5">h</span></span> <span class='ocr_word' id='word_1_91' title="bbox 2005 962 2011 970"><span class='xocr_word' id='xword_1_91' title="x_wconf -6">'</span></span> <span class='ocr_word' id='word_1_92' title="bbox 2051 948 2479 1005"><span class='xocr_word' id='xword_1_92' title="x_wconf -6">ht="natch_parent"</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_8' title="bbox 590 1328 2881 1594">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_16' title="bbox 590 1329 2880 1414"><span class='ocr_word' id='word_1_93' title="bbox 590 1349 704 1414"><span class='xocr_word' id='xword_1_93' title="x_wconf -3">Veja</span></span> <span class='ocr_word' id='word_1_94' title="bbox 732 1365 835 1414"><span class='xocr_word' id='xword_1_94' title="x_wconf -2">que</span></span> <span class='ocr_word' id='word_1_95' title="bbox 862 1366 894 1399"><span class='xocr_word' id='xword_1_95' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_96' title="bbox 921 1354 1226 1408"><span class='xocr_word' id='xword_1_96' title="x_wconf -1">LinearLayout</span></span> <span class='ocr_word' id='word_1_97' title="bbox 1255 1343 1468 1397"><span class='xocr_word' id='xword_1_97' title="x_wconf -3">recebeu</span></span> <span class='ocr_word' id='word_1_98' title="bbox 1496 1361 1587 1395"><span class='xocr_word' id='xword_1_98' title="x_wconf -1">um</span></span> <span class='ocr_word' id='word_1_99' title="bbox 1615 1334 1985 1393"><span class='xocr_word' id='xword_1_99' title="x_wconf -2">identicador</span></span> <span class='ocr_word' id='word_1_100' title="bbox 2009 1329 2772 1388"><span class='xocr_word' id='xword_1_100' title="x_wconf -2">android:id="@+id/layoutFrag",</span></span> <span class='ocr_word' id='word_1_101' title="bbox 2794 1347 2880 1398"><span class='xocr_word' id='xword_1_101' title="x_wconf -7">que</span></span></span>
<span class='ocr_line' id='line_1_17' title="bbox 590 1416 2881 1505"><span class='ocr_word' id='word_1_102' title="bbox 590 1439 702 1488"><span class='xocr_word' id='xword_1_102' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_103' title="bbox 728 1435 979 1489"><span class='xocr_word' id='xword_1_103' title="x_wconf -3">utilizado</span></span> <span class='ocr_word' id='word_1_104' title="bbox 1004 1455 1126 1505"><span class='xocr_word' id='xword_1_104' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_105' title="bbox 1151 1435 1418 1489"><span class='xocr_word' id='xword_1_105' title="x_wconf -2">adicionar</span></span> <span class='ocr_word' id='word_1_106' title="bbox 1440 1433 1887 1487"><span class='xocr_word' id='xword_1_106' title="x_wconf -2">dinamicamente</span></span> <span class='ocr_word' id='word_1_107' title="bbox 1912 1445 1945 1479"><span class='xocr_word' id='xword_1_107' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_108' title="bbox 1971 1423 2235 1493"><span class='xocr_word' id='xword_1_108' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_109' title="bbox 2262 1438 2416 1473"><span class='xocr_word' id='xword_1_109' title="x_wconf -2">nesse</span></span> <span class='ocr_word' id='word_1_110' title="bbox 2441 1416 2622 1487"><span class='xocr_word' id='xword_1_110' title="x_wconf -2">layout</span></span> <span class='ocr_word' id='word_1_111' title="bbox 2648 1416 2761 1486"><span class='xocr_word' id='xword_1_111' title="x_wconf -2">pela</span></span> <span class='ocr_word' id='word_1_112' title="bbox 2781 1424 2881 1476"><span class='xocr_word' id='xword_1_112' title="x_wconf -2">API.</span></span></span>
<span class='ocr_line' id='line_1_18' title="bbox 591 1511 2794 1594"><span class='ocr_word' id='word_1_113' title="bbox 591 1528 640 1578"><span class='xocr_word' id='xword_1_113' title="x_wconf -1">O</span></span> <span class='ocr_word' id='word_1_114' title="bbox 664 1525 852 1594"><span class='xocr_word' id='xword_1_114' title="x_wconf -2">cdigo</span></span> <span class='ocr_word' id='word_1_115' title="bbox 874 1525 940 1579"><span class='xocr_word' id='xword_1_115' title="x_wconf -2">da</span></span> <span class='ocr_word' id='word_1_116' title="bbox 963 1530 1167 1594"><span class='xocr_word' id='xword_1_116' title="x_wconf -2">activity</span></span> <span class='ocr_word' id='word_1_117' title="bbox 1189 1545 1292 1594"><span class='xocr_word' id='xword_1_117' title="x_wconf -2">que</span></span> <span class='ocr_word' id='word_1_118' title="bbox 1315 1530 1479 1578"><span class='xocr_word' id='xword_1_118' title="x_wconf -2">insere</span></span> <span class='ocr_word' id='word_1_119' title="bbox 1501 1544 1534 1576"><span class='xocr_word' id='xword_1_119' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_120' title="bbox 1557 1522 1816 1591"><span class='xocr_word' id='xword_1_120' title="x_wconf -3">fragment</span></span> <span class='ocr_word' id='word_1_121' title="bbox 1838 1538 1909 1572"><span class='xocr_word' id='xword_1_121' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_122' title="bbox 1932 1516 2112 1586"><span class='xocr_word' id='xword_1_122' title="x_wconf -1">layout</span></span> <span class='ocr_word' id='word_1_123' title="bbox 2135 1511 2280 1584"><span class='xocr_word' id='xword_1_123' title="x_wconf -1">pode</span></span> <span class='ocr_word' id='word_1_124' title="bbox 2302 1530 2386 1565"><span class='xocr_word' id='xword_1_124' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_125' title="bbox 2405 1525 2543 1563"><span class='xocr_word' id='xword_1_125' title="x_wconf -3">visto</span></span> <span class='ocr_word' id='word_1_126' title="bbox 2566 1529 2592 1563"><span class='xocr_word' id='xword_1_126' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_127' title="bbox 2617 1529 2794 1579"><span class='xocr_word' id='xword_1_127' title="x_wconf -3">seguir.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_9' title="bbox 594 1688 1153 1767">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_19' title="bbox 594 1688 1153 1767"><span class='ocr_word' id='word_1_128' title="bbox 767 1701 1153 1767"><span class='xocr_word' id='xword_1_128' title="x_wconf -2">MainActivity.java</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_10' title="bbox 591 1826 2660 2804">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_20' title="bbox 591 1827 2468 1890"><span class='ocr_word' id='word_1_129' title="bbox 591 1835 735 1889"><span class='xocr_word' id='xword_1_129' title="x_wconf -1">public</span></span> <span class='ocr_word' id='word_1_130' title="bbox 764 1836 884 1881"><span class='xocr_word' id='xword_1_130' title="x_wconf -1">class</span></span> <span class='ocr_word' id='word_1_131' title="bbox 912 1838 1207 1890"><span class='xocr_word' id='xword_1_131' title="x_wconf -3">HainActivity</span></span> <span class='ocr_word' id='word_1_132' title="bbox 1235 1836 1404 1880"><span class='xocr_word' id='xword_1_132' title="x_wconf -1">extends</span></span> <span class='ocr_word' id='word_1_133' title="bbox 1433 1830 2418 1889"><span class='xocr_word' id='xword_1_133' title="x_wconf -3">android.support.v4.app.FragnentActivity</span></span> <span class='ocr_word' id='word_1_134' title="bbox 2451 1827 2468 1883"><span class='xocr_word' id='xword_1_134' title="x_wconf -2">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_21' title="bbox 678 1919 898 1971"><span class='ocr_word' id='word_1_135' title="bbox 678 1919 898 1971"><span class='xocr_word' id='xword_1_135' title="x_wconf -3">@0verride</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_22' title="bbox 678 2001 1968 2056"><span class='ocr_word' id='word_1_136' title="bbox 678 2003 898 2056"><span class='xocr_word' id='xword_1_136' title="x_wconf -1">protected</span></span> <span class='ocr_word' id='word_1_137' title="bbox 926 2003 1021 2047"><span class='xocr_word' id='xword_1_137' title="x_wconf -3">void</span></span> <span class='ocr_word' id='word_1_138' title="bbox 1050 2002 1419 2055"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">onCreate(Bundle</span></span> <span class='ocr_word' id='word_1_139' title="bbox 1449 2002 1916 2054"><span class='xocr_word' id='xword_1_139' title="x_wconf -2">savedInstanceState)</span></span> <span class='ocr_word' id='word_1_140' title="bbox 1951 2001 1968 2056"><span class='xocr_word' id='xword_1_140' title="x_wconf -1">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_23' title="bbox 768 2086 1625 2139"><span class='ocr_word' id='word_1_141' title="bbox 768 2086 1625 2139"><span class='xocr_word' id='xword_1_141' title="x_wconf -2">super.onCreate(savedInstanceState);</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_24' title="bbox 768 2169 1726 2224"><span class='ocr_word' id='word_1_142' title="bbox 768 2169 1726 2224"><span class='xocr_word' id='xword_1_142' title="x_wconf -3">setContentView(R.layout.activity_main);</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_25' title="bbox 768 2251 1880 2309"><span class='ocr_word' id='word_1_143' title="bbox 768 2251 811 2305"><span class='xocr_word' id='xword_1_143' title="x_wconf 0">//</span></span> <span class='ocr_word' id='word_1_144' title="bbox 840 2252 1035 2296"><span class='xocr_word' id='xword_1_144' title="x_wconf -2">Adiciona</span></span> <span class='ocr_word' id='word_1_145' title="bbox 1064 2266 1085 2297"><span class='xocr_word' id='xword_1_145' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_146' title="bbox 1115 2252 1308 2307"><span class='xocr_word' id='xword_1_146' title="x_wconf -3">fragment</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1336 2253 1656 2298"><span class='xocr_word' id='xword_1_147' title="x_wconf -3">dinamicamente</span></span> <span class='ocr_word' id='word_1_148' title="bbox 1686 2254 1781 2309"><span class='xocr_word' id='xword_1_148' title="x_wconf -2">pela</span></span> <span class='ocr_word' id='word_1_149' title="bbox 1809 2259 1880 2299"><span class='xocr_word' id='xword_1_149' title="x_wconf -1">API</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_26' title="bbox 767 2335 1555 2391"><span class='ocr_word' id='word_1_150' title="bbox 767 2335 1283 2388"><span class='xocr_word' id='xword_1_150' title="x_wconf -2">if(savedInstanceState</span></span> <span class='ocr_word' id='word_1_151' title="bbox 1312 2354 1357 2372"><span class='xocr_word' id='xword_1_151' title="x_wconf -1">==</span></span> <span class='ocr_word' id='word_1_152' title="bbox 1387 2336 1503 2390"><span class='xocr_word' id='xword_1_152' title="x_wconf -1">null)</span></span> <span class='ocr_word' id='word_1_153' title="bbox 1538 2336 1555 2391"><span class='xocr_word' id='xword_1_153' title="x_wconf -2">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_27' title="bbox 854 2418 2660 2482"><span class='ocr_word' id='word_1_154' title="bbox 854 2418 1795 2477"><span class='xocr_word' id='xword_1_154' title="x_wconf -3">android.support.v4.app.FragnentHanager</span></span> <span class='ocr_word' id='word_1_155' title="bbox 1824 2423 1872 2468"><span class='xocr_word' id='xword_1_155' title="x_wconf -3">fn</span></span> <span class='ocr_word' id='word_1_156' title="bbox 1899 2441 1921 2461"><span class='xocr_word' id='xword_1_156' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_157' title="bbox 1950 2428 2660 2482"><span class='xocr_word' id='xword_1_157' title="x_wconf -3">getSupportFragnentHanager();</span></span></span>
<span class='ocr_line' id='line_1_28' title="bbox 856 2503 2016 2563"><span class='ocr_word' id='word_1_158' title="bbox 856 2504 1321 2555"><span class='xocr_word' id='xword_1_158' title="x_wconf -3">FragnentTransaction</span></span> <span class='ocr_word' id='word_1_159' title="bbox 1352 2503 1396 2547"><span class='xocr_word' id='xword_1_159' title="x_wconf -3">ft</span></span> <span class='ocr_word' id='word_1_160' title="bbox 1425 2522 1446 2539"><span class='xocr_word' id='xword_1_160' title="x_wconf 0">=</span></span> <span class='ocr_word' id='word_1_161' title="bbox 1475 2504 2016 2563"><span class='xocr_word' id='xword_1_161' title="x_wconf -3">fm.beginTransaction();</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 856 2585 1688 2643"><span class='ocr_word' id='word_1_162' title="bbox 856 2588 1073 2638"><span class='xocr_word' id='xword_1_162' title="x_wconf -3">Fragmentl</span></span> <span class='ocr_word' id='word_1_163' title="bbox 1103 2585 1221 2639"><span class='xocr_word' id='xword_1_163' title="x_wconf -2">frag1</span></span> <span class='ocr_word' id='word_1_164' title="bbox 1251 2604 1272 2621"><span class='xocr_word' id='xword_1_164' title="x_wconf 0">=</span></span> <span class='ocr_word' id='word_1_165' title="bbox 1301 2599 1372 2630"><span class='xocr_word' id='xword_1_165' title="x_wconf -3">new</span></span> <span class='ocr_word' id='word_1_166' title="bbox 1402 2590 1688 2643"><span class='xocr_word' id='xword_1_166' title="x_wconf -3">Fragment1();</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 855 2667 1914 2731"><span class='ocr_word' id='word_1_167' title="bbox 855 2667 1416 2724"><span class='xocr_word' id='xword_1_167' title="x_wconf -3">ft.add(R.id.layoutFrag,</span></span> <span class='ocr_word' id='word_1_168' title="bbox 1451 2671 1914 2731"><span class='xocr_word' id='xword_1_168' title="x_wconf -3">frag1,"Fragment1");</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_31' title="bbox 856 2750 1141 2804"><span class='ocr_word' id='word_1_169' title="bbox 856 2750 1141 2804"><span class='xocr_word' id='xword_1_169' title="x_wconf -4">ft.connit();</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 590 2831 2879 3210">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 767 2833 785 2887"><span class='ocr_word' id='word_1_170' title="bbox 767 2833 785 2887"><span class='xocr_word' id='xword_1_170' title="x_wconf -3">}</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 678 2917 696 2972"><span class='ocr_word' id='word_1_171' title="bbox 678 2917 696 2972"><span class='xocr_word' id='xword_1_171' title="x_wconf -3">}</span></span></span>
<span class='ocr_line' id='line_1_34' title="bbox 678 3000 696 3054"><span class='ocr_word' id='word_1_172' title="bbox 678 3000 696 3054"><span class='xocr_word' id='xword_1_172' title="x_wconf -3">}</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 590 3116 2879 3209"><span class='ocr_word' id='word_1_173' title="bbox 590 3121 713 3170"><span class='xocr_word' id='xword_1_173' title="x_wconf -1">Para</span></span> <span class='ocr_word' id='word_1_174' title="bbox 747 3120 946 3178"><span class='xocr_word' id='xword_1_174' title="x_wconf -2">inserir,</span></span> <span class='ocr_word' id='word_1_175' title="bbox 974 3116 1256 3170"><span class='xocr_word' id='xword_1_175' title="x_wconf -2">substituir</span></span> <span class='ocr_word' id='word_1_176' title="bbox 1285 3138 1354 3172"><span class='xocr_word' id='xword_1_176' title="x_wconf -1">ou</span></span> <span class='ocr_word' id='word_1_177' title="bbox 1388 3140 1621 3178"><span class='xocr_word' id='xword_1_177' title="x_wconf -2">remover</span></span> <span class='ocr_word' id='word_1_178' title="bbox 1651 3147 1743 3183"><span class='xocr_word' id='xword_1_178' title="x_wconf -2">um</span></span> <span class='ocr_word' id='word_1_179' title="bbox 1777 3130 2042 3204"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_180' title="bbox 2074 3140 2194 3209"><span class='xocr_word' id='xword_1_180' title="x_wconf -1">pela</span></span> <span class='ocr_word' id='word_1_181' title="bbox 2224 3146 2330 3197"><span class='xocr_word' id='xword_1_181' title="x_wconf -2">API</span></span> <span class='ocr_word' id='word_1_182' title="bbox 2365 3148 2393 3198"><span class='xocr_word' id='xword_1_182' title="x_wconf -2"></span></span> <span class='ocr_word' id='word_1_183' title="bbox 2425 3144 2682 3200"><span class='xocr_word' id='xword_1_183' title="x_wconf -3">utilizada</span></span> <span class='ocr_word' id='word_1_184' title="bbox 2715 3163 2741 3198"><span class='xocr_word' id='xword_1_184' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2772 3140 2879 3195"><span class='xocr_word' id='xword_1_185' title="x_wconf -2">clas-</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 590 3212 2881 3307">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_36' title="bbox 590 3214 2880 3306"><span class='ocr_word' id='word_1_186' title="bbox 590 3226 645 3260"><span class='xocr_word' id='xword_1_186' title="x_wconf -1">se</span></span> <span class='ocr_word' id='word_1_187' title="bbox 679 3214 1236 3270"><span class='xocr_word' id='xword_1_187' title="x_wconf -5">Fragnenransaction,</span></span> <span class='ocr_word' id='word_1_188' title="bbox 1268 3228 1295 3261"><span class='xocr_word' id='xword_1_188' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_189' title="bbox 1328 3229 1360 3262"><span class='xocr_word' id='xword_1_189' title="x_wconf -1">0</span></span> <span class='ocr_word' id='word_1_190' title="bbox 1395 3214 1619 3270"><span class='xocr_word' id='xword_1_190' title="x_wconf -2">mtodo</span></span> <span class='ocr_word' id='word_1_191' title="bbox 1653 3230 1875 3287"><span class='xocr_word' id='xword_1_191' title="x_wconf -3">connit()</span></span> <span class='ocr_word' id='word_1_192' title="bbox 1913 3226 2106 3285"><span class='xocr_word' id='xword_1_192' title="x_wconf -2">efetiva</span></span> <span class='ocr_word' id='word_1_193' title="bbox 2142 3251 2200 3287"><span class='xocr_word' id='xword_1_193' title="x_wconf -1">as</span></span> <span class='ocr_word' id='word_1_194' title="bbox 2235 3234 2553 3306"><span class='xocr_word' id='xword_1_194' title="x_wconf -2">alteraes.</span></span> <span class='ocr_word' id='word_1_195' title="bbox 2589 3241 2639 3292"><span class='xocr_word' id='xword_1_195' title="x_wconf -1">O</span></span> <span class='ocr_word' id='word_1_196' title="bbox 2676 3228 2880 3291"><span class='xocr_word' id='xword_1_196' title="x_wconf -5">mtodo</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_13' title="bbox 589 3295 2880 3400">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_37' title="bbox 590 3297 2880 3399"><span class='ocr_word' id='word_1_197' title="bbox 590 3302 923 3359"><span class='xocr_word' id='xword_1_197' title="x_wconf -5">add(1y0U'C,ff</span></span> <span class='ocr_word' id='word_1_198' title="bbox 929 3303 1104 3358"><span class='xocr_word' id='xword_1_198' title="x_wconf -6">9.'C9)</span></span> <span class='ocr_word' id='word_1_199' title="bbox 1134 3297 1311 3351"><span class='xocr_word' id='xword_1_199' title="x_wconf -2">recebe</span></span> <span class='ocr_word' id='word_1_200' title="bbox 1336 3319 1493 3356"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">como</span></span> <span class='ocr_word' id='word_1_201' title="bbox 1519 3312 1814 3373"><span class='xocr_word' id='xword_1_201' title="x_wconf -2">parmetro</span></span> <span class='ocr_word' id='word_1_202' title="bbox 1840 3336 1873 3370"><span class='xocr_word' id='xword_1_202' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_203' title="bbox 1900 3318 2277 3380"><span class='xocr_word' id='xword_1_203' title="x_wconf -2">identicador</span></span> <span class='ocr_word' id='word_1_204' title="bbox 2301 3327 2375 3382"><span class='xocr_word' id='xword_1_204' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_205' title="bbox 2402 3328 2584 3399"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">layout</span></span> <span class='ocr_word' id='word_1_206' title="bbox 2610 3329 2677 3384"><span class='xocr_word' id='xword_1_206' title="x_wconf -2">de</span></span> <span class='ocr_word' id='word_1_207' title="bbox 2702 3324 2834 3383"><span class='xocr_word' id='xword_1_207' title="x_wconf -6">ond</span></span> <span class='ocr_word' id='word_1_208' title="bbox 2855 3336 2880 3370"><span class='xocr_word' id='xword_1_208' title="x_wconf -3">0</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 591 3382 2880 3491">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_38' title="bbox 591 3384 2880 3489"><span class='ocr_word' id='word_1_209' title="bbox 591 3386 848 3455"><span class='xocr_word' id='xword_1_209' title="x_wconf -8">ffagmem</span></span> <span class='ocr_word' id='word_1_210' title="bbox 871 3384 997 3438"><span class='xocr_word' id='xword_1_210' title="x_wconf -7">deve</span></span> <span class='ocr_word' id='word_1_211' title="bbox 1020 3405 1100 3438"><span class='xocr_word' id='xword_1_211' title="x_wconf -7">Sf</span></span> <span class='ocr_word' id='word_1_212' title="bbox 1122 3387 1365 3451"><span class='xocr_word' id='xword_1_212' title="x_wconf -5">lSr1d0,</span></span> <span class='ocr_word' id='word_1_213' title="bbox 1386 3410 1412 3444"><span class='xocr_word' id='xword_1_213' title="x_wconf -4">a</span></span> <span class='ocr_word' id='word_1_214' title="bbox 1439 3395 1693 3454"><span class='xocr_word' id='xword_1_214' title="x_wconf -1">instncia</span></span> <span class='ocr_word' id='word_1_215' title="bbox 1716 3402 1788 3458"><span class='xocr_word' id='xword_1_215' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_216' title="bbox 1812 3406 1993 3477"><span class='xocr_word' id='xword_1_216' title="x_wconf -2">objeto</span></span> <span class='ocr_word' id='word_1_217' title="bbox 2017 3412 2090 3467"><span class='xocr_word' id='xword_1_217' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_218' title="bbox 2116 3414 2382 3488"><span class='xocr_word' id='xword_1_218' title="x_wconf -3">fragment</span></span> <span class='ocr_word' id='word_1_219' title="bbox 2405 3441 2433 3476"><span class='xocr_word' id='xword_1_219' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_220' title="bbox 2458 3441 2584 3477"><span class='xocr_word' id='xword_1_220' title="x_wconf -2">uma</span></span> <span class='ocr_word' id='word_1_221' title="bbox 2610 3425 2774 3489"><span class='xocr_word' id='xword_1_221' title="x_wconf -2">string</span></span> <span class='ocr_word' id='word_1_222' title="bbox 2794 3426 2880 3484"><span class='xocr_word' id='xword_1_222' title="x_wconf -6">qUf</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_15' title="bbox 590 3473 1689 3632">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_39' title="bbox 590 3474 1689 3544"><span class='ocr_word' id='word_1_223' title="bbox 590 3481 615 3529"><span class='xocr_word' id='xword_1_223' title="x_wconf -2"></span></span> <span class='ocr_word' id='word_1_224' title="bbox 638 3495 665 3529"><span class='xocr_word' id='xword_1_224' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_225' title="bbox 691 3491 787 3544"><span class='xocr_word' id='xword_1_225' title="x_wconf -2">tag.</span></span> <span class='ocr_word' id='word_1_226' title="bbox 806 3479 851 3527"><span class='xocr_word' id='xword_1_226' title="x_wconf -1">A</span></span> <span class='ocr_word' id='word_1_227' title="bbox 871 3490 956 3543"><span class='xocr_word' id='xword_1_227' title="x_wconf -1">tag</span></span> <span class='ocr_word' id='word_1_228' title="bbox 978 3474 1118 3543"><span class='xocr_word' id='xword_1_228' title="x_wconf -1">pode</span></span> <span class='ocr_word' id='word_1_229' title="bbox 1140 3495 1221 3528"><span class='xocr_word' id='xword_1_229' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_230' title="bbox 1242 3477 1488 3535"><span class='xocr_word' id='xword_1_230' title="x_wconf -2">utilizada</span></span> <span class='ocr_word' id='word_1_231' title="bbox 1551 3505 1689 3544"><span class='xocr_word' id='xword_1_231' title="x_wconf -6">oszer</span></span></span>
<span class='ocr_line' id='line_1_40' title="bbox 590 3564 1441 3632"><span class='ocr_word' id='word_1_232' title="bbox 590 3586 621 3619"><span class='xocr_word' id='xword_1_232' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_233' title="bbox 644 3564 861 3618"><span class='xocr_word' id='xword_1_233' title="x_wconf -2">mtodo</span></span> <span class='ocr_word' id='word_1_234' title="bbox 882 3570 1441 3632"><span class='xocr_word' id='xword_1_234' title="x_wconf -3">ndFragnentByTag(tag).</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_16' title="bbox 1514 3495 2879 3587">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_41' title="bbox 1514 3496 2879 3586"><span class='ocr_word' id='word_1_235' title="bbox 1514 3504 1547 3554"><span class='xocr_word' id='xword_1_235' title="x_wconf -4">P</span></span> <span class='ocr_word' id='word_1_236' title="bbox 1696 3496 1954 3555"><span class='xocr_word' id='xword_1_236' title="x_wconf -7">OTITICHC</span></span> <span class='ocr_word' id='word_1_237' title="bbox 1979 3523 2104 3573"><span class='xocr_word' id='xword_1_237' title="x_wconf -1">para</span></span> <span class='ocr_word' id='word_1_238' title="bbox 2129 3527 2414 3566"><span class='xocr_word' id='xword_1_238' title="x_wconf -5">I1COU`8I`</span></span> <span class='ocr_word' id='word_1_239' title="bbox 2437 3533 2470 3567"><span class='xocr_word' id='xword_1_239' title="x_wconf -1">O</span></span> <span class='ocr_word' id='word_1_240' title="bbox 2496 3513 2756 3586"><span class='xocr_word' id='xword_1_240' title="x_wconf -5">fI'3gITl(I1</span></span> <span class='ocr_word' id='word_1_241' title="bbox 2779 3518 2879 3561"><span class='xocr_word' id='xword_1_241' title="x_wconf -5">COITI</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_17' title="bbox 678 3698 2245 3794">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_42' title="bbox 678 3699 2245 3793"><span class='ocr_word' id='word_1_242' title="bbox 678 3704 896 3754"><span class='xocr_word' id='xword_1_242' title="x_wconf -3">Fragmentl</span></span> <span class='ocr_word' id='word_1_243' title="bbox 927 3699 1045 3754"><span class='xocr_word' id='xword_1_243' title="x_wconf -3">fragl</span></span> <span class='ocr_word' id='word_1_244' title="bbox 1074 3717 1096 3735"><span class='xocr_word' id='xword_1_244' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_245' title="bbox 1127 3700 1390 3758"><span class='xocr_word' id='xword_1_245' title="x_wconf -3">(Fragmentl)</span></span> <span class='ocr_word' id='word_1_246' title="bbox 1423 3706 2245 3793"><span class='xocr_word' id='xword_1_246' title="x_wconf -6">fn.ndFragmentByTag("Fra9ment1~).</span></span></span>
</p>
</div>
</div>
</body>
</html>
(api
I 8 F uo
t n ragmen s 255
Repare que, antes de inserir o fra gmt pela API foi validada a condi o
lsavedlnstancestate == "u) Para ter Certeza de que a activity estava sendo crida
nesse instante. Se o Bundle no estiver nulo signica ue a act- _t f . d ,d
criada novamente. Isso pode acontecer a q M y O1 estrul a 6
I ' o , ,. ,, .
O girar o dispositivo trocando a orientao
de vertical para horizontal, ou vice-versa
mesmo Bundl ' , . Quando uma activity destruda, o m
todo onSaveInstanceState(bundle) chamad O para o aplicativo salvar o estado da tela.
E556 C Pa55ad0 C0m0 argumento para o metodo onCreate(bundle) ao
facflaf a aCt1V1tY Por 1550, devemos testar a condio if(savedInstanceState == null)
para ter Certeza de que a activity est sendo criada pela primeira vez pois caso
contrario o fragment seria adicionado duas vezes no layout Portanto lembre~se
a transao criada pl0 F"H9P1entTransaction persistida durante o ciclo de vida da
activity e qualquer troca de orientao.
MainActivity.java
Dublc class MainActivity extends android.support.v7.app.AppCompatActtv1.ty {
@0verride
protected void onCreate(Bundl e savedInstance5tate) {
super.onCreate(savedInstance5tate);.
setContentView(R.layout.actvity_matn);
256 Google Android - 4 edio
ActionBar actionBar = getSupportActionBar(); MODE TABS).
actonBar.setNavgationHode(androd.app.ActionBar.NAVIGATION_ _ ,
// Tab 1
ActionBar.Tab tab1 = actionBar.newTab().setTet("Fra9 1 );
tab1.setTabLstener(new MyTabLstener(ths, new Fra9@t1()));
actonBar.addTab(tab1);
// Tab 2
ActonBar.Tab tab2 = actonBar.newTab().setTet("Frag 2");
tab2.setTabListener(new MyTabLstener(ths, new Fragnent2()));g
actonBar.addTab(tab2);
// Tab 3
ActionBar.Tab tab3 = actonBar.newTab().setText("Frag 3");
tab3.setTabLstener(new MyTabLstener(ths, new Fragment3()));
actonBar.addTab(tab3);
}
/res/layout/activity_main.xmI
<FrameLayout m1ns:android="http://schemas.android.com/apk/res/android"
xnlns:too1s="http://schemas.androd.com/tools"
androd:layout_wdth="match_parent" android:1ayout_heght="natch_parent"
androd:id="@+id/1ayoutFrag">
</span></span> <span class='ocr_word' id='word_1_118' title="bbox 817 2714 1010 2763"><span class='xocr_word' id='xword_1_118' title="x_wconf -3">Fragment</span></span> <span class='ocr_word' id='word_1_119' title="bbox 1041 2708 1135 2752"><span class='xocr_word' id='xword_1_119' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_120' title="bbox 1165 2706 1360 2751"><span class='xocr_word' id='xword_1_120' title="x_wconf -4">inserido</span></span> <span class='ocr_word' id='word_1_121' title="bbox 1389 2707 1484 2759"><span class='xocr_word' id='xword_1_121' title="x_wconf -4">aqui</span></span> <span class='ocr_word' id='word_1_122' title="bbox 1513 2719 1559 2750"><span class='xocr_word' id='xword_1_122' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_123' title="bbox 1588 2706 1735 2760"><span class='xocr_word' id='xword_1_123' title="x_wconf -4">layout</span></span> <span class='ocr_word' id='word_1_124' title="bbox 1768 2705 2189 2761"><span class='xocr_word' id='xword_1_124' title="x_wconf -4">"@+id/1ayoutFrag"</span></span> <span class='ocr_word' id='word_1_125' title="bbox 2254 2731 2266 2737"><span class='xocr_word' id='xword_1_125' title="x_wconf -1">-</span></span> <span class='ocr_word' id='word_1_126' title="bbox 2302 2721 2323 2746"><span class='xocr_word' id='xword_1_126' title="x_wconf 0"></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 603 2795 949 2849"><span class='ocr_word' id='word_1_127' title="bbox 603 2795 949 2849"><span class='xocr_word' id='xword_1_127' title="x_wconf -2"></FrameLayout</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 603 2887 2899 3275">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 604 2905 2895 2981"><span class='ocr_word' id='word_1_128' title="bbox 604 2916 675 2965"><span class='xocr_word' id='xword_1_128' title="x_wconf -1">As</span></span> <span class='ocr_word' id='word_1_129' title="bbox 699 2910 882 2964"><span class='xocr_word' id='xword_1_129' title="x_wconf -1">classes</span></span> <span class='ocr_word' id='word_1_130' title="bbox 908 2919 1150 2972"><span class='xocr_word' id='xword_1_130' title="x_wconf -3">Fragnentl,</span></span> <span class='ocr_word' id='word_1_131' title="bbox 1174 2917 1398 2970"><span class='xocr_word' id='xword_1_131' title="x_wconf -2">Fragnent2</span></span> <span class='ocr_word' id='word_1_132' title="bbox 1423 2927 1450 2960"><span class='xocr_word' id='xword_1_132' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_133' title="bbox 1475 2918 1701 2971"><span class='xocr_word' id='xword_1_133' title="x_wconf -2">Fragnent3</span></span> <span class='ocr_word' id='word_1_134' title="bbox 1728 2916 1830 2962"><span class='xocr_word' id='xword_1_134' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_135' title="bbox 1855 2917 2004 2964"><span class='xocr_word' id='xword_1_135' title="x_wconf -2">sero</span></span> <span class='ocr_word' id='word_1_136' title="bbox 2029 2910 2267 2965"><span class='xocr_word' id='xword_1_136' title="x_wconf -2">exibidas</span></span> <span class='ocr_word' id='word_1_137' title="bbox 2292 2914 2418 2981"><span class='xocr_word' id='xword_1_137' title="x_wconf -1">aqui</span></span> <span class='ocr_word' id='word_1_138' title="bbox 2443 2929 2568 2980"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_139' title="bbox 2594 2905 2895 2963"><span class='xocr_word' id='xword_1_139' title="x_wconf -2">economizar</span></span></span>
<span class='ocr_line' id='line_1_31' title="bbox 607 2997 2895 3072"><span class='ocr_word' id='word_1_140' title="bbox 607 3020 791 3070"><span class='xocr_word' id='xword_1_140' title="x_wconf -3">espao</span></span> <span class='ocr_word' id='word_1_141' title="bbox 819 3019 888 3052"><span class='xocr_word' id='xword_1_141' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_142' title="bbox 915 2998 1049 3061"><span class='xocr_word' id='xword_1_142' title="x_wconf -1">livro,</span></span> <span class='ocr_word' id='word_1_143' title="bbox 1074 3002 1189 3067"><span class='xocr_word' id='xword_1_143' title="x_wconf -1">pois</span></span> <span class='ocr_word' id='word_1_144' title="bbox 1215 2997 1318 3050"><span class='xocr_word' id='xword_1_144' title="x_wconf -1">elas</span></span> <span class='ocr_word' id='word_1_145' title="bbox 1343 3005 1434 3051"><span class='xocr_word' id='xword_1_145' title="x_wconf -1">so</span></span> <span class='ocr_word' id='word_1_146' title="bbox 1460 2998 1671 3068"><span class='xocr_word' id='xword_1_146' title="x_wconf -1">simples</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1697 3019 1855 3054"><span class='xocr_word' id='xword_1_147' title="x_wconf -1">como</span></span> <span class='ocr_word' id='word_1_148' title="bbox 1882 3021 1909 3054"><span class='xocr_word' id='xword_1_148' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_149' title="bbox 1936 3001 2099 3056"><span class='xocr_word' id='xword_1_149' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_150' title="bbox 2127 3013 2359 3067"><span class='xocr_word' id='xword_1_150' title="x_wconf -3">Fragnentl</span></span> <span class='ocr_word' id='word_1_151' title="bbox 2387 3022 2493 3072"><span class='xocr_word' id='xword_1_151' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_152' title="bbox 2520 3000 2820 3056"><span class='xocr_word' id='xword_1_152' title="x_wconf -6">estudamos</span></span> <span class='ocr_word' id='word_1_153' title="bbox 2844 3004 2895 3042"><span class='xocr_word' id='xword_1_153' title="x_wconf -1">no</span></span></span>
<span class='ocr_line' id='line_1_32' title="bbox 608 3087 2897 3165"><span class='ocr_word' id='word_1_154' title="bbox 608 3089 841 3159"><span class='xocr_word' id='xword_1_154' title="x_wconf -1">exemplo</span></span> <span class='ocr_word' id='word_1_155' title="bbox 856 3093 1082 3142"><span class='xocr_word' id='xword_1_155' title="x_wconf -1">anterior.</span></span> <span class='ocr_word' id='word_1_156' title="bbox 1100 3087 1425 3141"><span class='xocr_word' id='xword_1_156' title="x_wconf -1">Lembrando</span></span> <span class='ocr_word' id='word_1_157' title="bbox 1441 3108 1542 3158"><span class='xocr_word' id='xword_1_157' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_158' title="bbox 1557 3090 1683 3144"><span class='xocr_word' id='xword_1_158' title="x_wconf -2">cada</span></span> <span class='ocr_word' id='word_1_159' title="bbox 1701 3090 1955 3162"><span class='xocr_word' id='xword_1_159' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_160' title="bbox 1971 3109 2075 3147"><span class='xocr_word' id='xword_1_160' title="x_wconf -1">tem</span></span> <span class='ocr_word' id='word_1_161' title="bbox 2091 3114 2184 3148"><span class='xocr_word' id='xword_1_161' title="x_wconf -1">seu</span></span> <span class='ocr_word' id='word_1_162' title="bbox 2200 3099 2414 3165"><span class='xocr_word' id='xword_1_162' title="x_wconf -2">prprio</span></span> <span class='ocr_word' id='word_1_163' title="bbox 2430 3098 2646 3165"><span class='xocr_word' id='xword_1_163' title="x_wconf -2">arquivo</span></span> <span class='ocr_word' id='word_1_164' title="bbox 2661 3091 2726 3146"><span class='xocr_word' id='xword_1_164' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_165' title="bbox 2740 3089 2897 3156"><span class='xocr_word' id='xword_1_165' title="x_wconf -8">layout,</span></span></span>
<span class='ocr_line' id='line_1_33' title="bbox 610 3178 2881 3255"><span class='ocr_word' id='word_1_166' title="bbox 610 3201 705 3251"><span class='xocr_word' id='xword_1_166' title="x_wconf -5">P0f</span></span> <span class='ocr_word' id='word_1_167' title="bbox 723 3179 974 3248"><span class='xocr_word' id='xword_1_167' title="x_wconf -6">XmPl0</span></span> <span class='ocr_word' id='word_1_168' title="bbox 996 3181 1577 3246"><span class='xocr_word' id='xword_1_168' title="x_wconf -8">/TCS/<1)'0WfT1._Zm611_1</span></span> <span class='ocr_word' id='word_1_169' title="bbox 1592 3225 1603 3244"><span class='xocr_word' id='xword_1_169' title="x_wconf -1">,</span></span> <span class='ocr_word' id='word_1_170' title="bbox 1618 3186 2218 3254"><span class='xocr_word' id='xword_1_170' title="x_wconf -4">/res/layout_fragment_2</span></span> <span class='ocr_word' id='word_1_171' title="bbox 2241 3206 2268 3241"><span class='xocr_word' id='xword_1_171' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_172' title="bbox 2286 3178 2881 3255"><span class='xocr_word' id='xword_1_172' title="x_wconf -3">/res/layout_ragment_3-</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_15' title="bbox 608 3282 2903 3683">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 611 3302 2898 3412"><span class='ocr_word' id='word_1_173' title="bbox 611 3309 661 3360"><span class='xocr_word' id='xword_1_173' title="x_wconf -4">O</span></span> <span class='ocr_word' id='word_1_174' title="bbox 682 3304 899 3412"><span class='xocr_word' id='xword_1_174' title="x_wconf -10">Segfedo</span></span> <span class='ocr_word' id='word_1_175' title="bbox 920 3323 1042 3373"><span class='xocr_word' id='xword_1_175' title="x_wconf -8">para</span></span> <span class='ocr_word' id='word_1_176' title="bbox 1064 3303 1266 3356"><span class='xocr_word' id='xword_1_176' title="x_wconf -7">Uuhzaf</span></span> <span class='ocr_word' id='word_1_177' title="bbox 1284 3323 1344 3356"><span class='xocr_word' id='xword_1_177' title="x_wconf -5">05</span></span> <span class='ocr_word' id='word_1_178' title="bbox 1365 3302 1649 3374"><span class='xocr_word' id='xword_1_178' title="x_wconf -6">ffagmfints</span></span> <span class='ocr_word' id='word_1_179' title="bbox 1669 3328 1790 3363"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">com</span></span> <span class='ocr_word' id='word_1_180' title="bbox 1811 3330 1866 3365"><span class='xocr_word' id='xword_1_180' title="x_wconf -1">as</span></span> <span class='ocr_word' id='word_1_181' title="bbox 1887 3311 2006 3366"><span class='xocr_word' id='xword_1_181' title="x_wconf -2">tabs</span></span> <span class='ocr_word' id='word_1_182' title="bbox 2026 3317 2053 3367"><span class='xocr_word' id='xword_1_182' title="x_wconf -2"></span></span> <span class='ocr_word' id='word_1_183' title="bbox 2074 3333 2107 3367"><span class='xocr_word' id='xword_1_183' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_184' title="bbox 2126 3321 2436 3369"><span class='xocr_word' id='xword_1_184' title="x_wconf -4">TabLstener.</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2451 3318 2571 3385"><span class='xocr_word' id='xword_1_185' title="x_wconf -1">Veja</span></span> <span class='ocr_word' id='word_1_186' title="bbox 2593 3332 2699 3385"><span class='xocr_word' id='xword_1_186' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_187' title="bbox 2719 3330 2745 3365"><span class='xocr_word' id='xword_1_187' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_188' title="bbox 2764 3305 2898 3362"><span class='xocr_word' id='xword_1_188' title="x_wconf -6">clas</span></span></span>
<span class='ocr_line' id='line_1_35' title="bbox 610 3393 2900 3475"><span class='ocr_word' id='word_1_189' title="bbox 610 3402 945 3460"><span class='xocr_word' id='xword_1_189' title="x_wconf -4">MyTabL1stener</span></span> <span class='ocr_word' id='word_1_190' title="bbox 970 3393 1148 3447"><span class='xocr_word' id='xword_1_190' title="x_wconf -1">recebe</span></span> <span class='ocr_word' id='word_1_191' title="bbox 1173 3413 1242 3446"><span class='xocr_word' id='xword_1_191' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_192' title="bbox 1265 3413 1359 3447"><span class='xocr_word' id='xword_1_192' title="x_wconf -1">seu</span></span> <span class='ocr_word' id='word_1_193' title="bbox 1381 3413 1683 3452"><span class='xocr_word' id='xword_1_193' title="x_wconf -2">construtor</span></span> <span class='ocr_word' id='word_1_194' title="bbox 1705 3420 1738 3454"><span class='xocr_word' id='xword_1_194' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_195' title="bbox 1763 3400 2026 3473"><span class='xocr_word' id='xword_1_195' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_196' title="bbox 2049 3425 2156 3475"><span class='xocr_word' id='xword_1_196' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_197' title="bbox 2179 3406 2261 3461"><span class='xocr_word' id='xword_1_197' title="x_wconf -2">ela</span></span> <span class='ocr_word' id='word_1_198' title="bbox 2284 3406 2415 3461"><span class='xocr_word' id='xword_1_198' title="x_wconf -1">deve</span></span> <span class='ocr_word' id='word_1_199' title="bbox 2439 3423 2670 3462"><span class='xocr_word' id='xword_1_199' title="x_wconf -2">mostrar</span></span> <span class='ocr_word' id='word_1_200' title="bbox 2691 3422 2754 3459"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">ao</span></span> <span class='ocr_word' id='word_1_201' title="bbox 2777 3397 2900 3452"><span class='xocr_word' id='xword_1_201' title="x_wconf -3">clicar</span></span></span>
<span class='ocr_line' id='line_1_36' title="bbox 612 3479 2900 3568"><span class='ocr_word' id='word_1_202' title="bbox 612 3505 695 3539"><span class='xocr_word' id='xword_1_202' title="x_wconf -1">em</span></span> <span class='ocr_word' id='word_1_203' title="bbox 716 3484 845 3538"><span class='xocr_word' id='xword_1_203' title="x_wconf -1">cada</span></span> <span class='ocr_word' id='word_1_204' title="bbox 865 3483 963 3537"><span class='xocr_word' id='xword_1_204' title="x_wconf -2">tab.</span></span> <span class='ocr_word' id='word_1_205' title="bbox 978 3487 1094 3552"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">Veja</span></span> <span class='ocr_word' id='word_1_206' title="bbox 1114 3502 1218 3552"><span class='xocr_word' id='xword_1_206' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_207' title="bbox 1237 3483 1384 3538"><span class='xocr_word' id='xword_1_207' title="x_wconf -2">basta</span></span> <span class='ocr_word' id='word_1_208' title="bbox 1407 3505 1530 3541"><span class='xocr_word' id='xword_1_208' title="x_wconf -1">uma</span></span> <span class='ocr_word' id='word_1_209' title="bbox 1551 3492 1706 3545"><span class='xocr_word' id='xword_1_209' title="x_wconf -1">nica</span></span> <span class='ocr_word' id='word_1_210' title="bbox 1727 3491 1875 3548"><span class='xocr_word' id='xword_1_210' title="x_wconf -2">linha</span></span> <span class='ocr_word' id='word_1_211' title="bbox 1895 3494 1962 3549"><span class='xocr_word' id='xword_1_211' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_212' title="bbox 1981 3496 2175 3567"><span class='xocr_word' id='xword_1_212' title="x_wconf -2">cdigo</span></span> <span class='ocr_word' id='word_1_213' title="bbox 2196 3518 2323 3568"><span class='xocr_word' id='xword_1_213' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_214' title="bbox 2343 3515 2593 3554"><span class='xocr_word' id='xword_1_214' title="x_wconf -5">executar</span></span> <span class='ocr_word' id='word_1_215' title="bbox 2609 3518 2642 3553"><span class='xocr_word' id='xword_1_215' title="x_wconf -4">0</span></span> <span class='ocr_word' id='word_1_216' title="bbox 2662 3479 2900 3552"><span class='xocr_word' id='xword_1_216' title="x_wconf -6">eomand</span></span></span>
<span class='ocr_line' id='line_1_37' title="bbox 615 3578 2857 3662"><span class='ocr_word' id='word_1_217' title="bbox 615 3579 1337 3638"><span class='xocr_word' id='xword_1_217' title="x_wconf -5">Fragnenransacton.rep1ace(</span></span> <span class='ocr_word' id='word_1_218' title="bbox 1352 3617 1360 3627"><span class='xocr_word' id='xword_1_218' title="x_wconf 0">.</span></span> <span class='ocr_word' id='word_1_219' title="bbox 1378 3618 1386 3628"><span class='xocr_word' id='xword_1_219' title="x_wconf 0">.</span></span> <span class='ocr_word' id='word_1_220' title="bbox 1404 3582 1442 3638"><span class='xocr_word' id='xword_1_220' title="x_wconf 0">.)</span></span> <span class='ocr_word' id='word_1_221' title="bbox 1467 3597 1493 3630"><span class='xocr_word' id='xword_1_221' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_222' title="bbox 1515 3579 1797 3637"><span class='xocr_word' id='xword_1_222' title="x_wconf -2">substituir</span></span> <span class='ocr_word' id='word_1_223' title="bbox 1816 3605 1851 3639"><span class='xocr_word' id='xword_1_223' title="x_wconf -2">o</span></span> <span class='ocr_word' id='word_1_224' title="bbox 1873 3585 2137 3657"><span class='xocr_word' id='xword_1_224' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_225' title="bbox 2160 3609 2226 3644"><span class='xocr_word' id='xword_1_225' title="x_wconf -2">ao</span></span> <span class='ocr_word' id='word_1_226' title="bbox 2248 3590 2407 3645"><span class='xocr_word' id='xword_1_226' title="x_wconf -2">clicar</span></span> <span class='ocr_word' id='word_1_227' title="bbox 2427 3610 2513 3646"><span class='xocr_word' id='xword_1_227' title="x_wconf -1">em</span></span> <span class='ocr_word' id='word_1_228' title="bbox 2537 3590 2748 3662"><span class='xocr_word' id='xword_1_228' title="x_wconf -2">alguma</span></span> <span class='ocr_word' id='word_1_229' title="bbox 2771 3578 2857 3638"><span class='xocr_word' id='xword_1_229' title="x_wconf -4">tab-</span></span></span>
</p>
</div>
</div>
</body>
</html>
Captulo 8 n Fragments
257
MyTabListener.java
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
@0verride
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// Troca o fragment dinamicamente ao clicar na tab
ft.replace(R.id.layoutFrag, this.frag, null);
}
Ao executar esse projeto, o resultado deve ser como a gura 8.10, que mostra a
segunda tab selecionada e o Fragnent2 no layout.
Fg2
LI MainActvity.java
/res/layout/activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<android.support.v4.view.ViewPager android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_height="wrap_content" />
TabsAdapter.java
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
QLC class TabsAdapter extends FragmentPagerAdapter {
public TabsAdapter(FragmentManager supportFragmentManager) {
super(supportFragmentManager);
}
@0verride
public int getCount() {
// 0 ViewPager vai ter 3 pginas
return 3;
}
260 Google Android - 4 edio
@0verride
public Fragment getItem(int idx) {
if(id == G) {
return new Fragment1();
} else if(id == 1) {
return new Fragment2();
}
Esse adapter apenas fornece o contedo do ViewPager; teremos trs pginas, e cada
uma um fragment. Para concluir o exemplo, a classe MyTabListener que trata dos
eventos das tabs foi alterada, para que, quando selecionar uma tab, a pgina do
ViewPager seja atualizada com o ndice da tab selecionada. Lembre-se de que neste
exemplo o ViewPager quem controla tudo, e as Tabs so meras coadjuvantes.
MyTabListener.java
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
public class MyTabListener implements ActionBar.TabListener {
private ViewPager viewPager;
private int idx;
public MyTabListener(ViewPager viewPager, int idx) {
this.viewPager = viewPager;
this.id = idx;
}
@Override
@0verride
Nota: a action
vendo al ' fb-' -, .,. .
af COITI tabs f0l descontmuada (deprecated); assim, voce deve estar
guns alertas (wammgs) DO codio. Mas este exemplo e um classico que
Captulo 8 I Fragments 261
voc precisa conhecer, at porque ele tambm mostrou como utilizar o ViewPager +
Fragments. A tab aqui mera coadjuvante e pode ser substituda por qualquer outro
componente. Quando formos desenvolver o projeto dos carros vamos utilizar o
componente TabLayout da biblioteca Android Design Support Library.
.._;
'..,,3
. z..
`'l` WW "
5- V- f l `.g
-zap-z I.. * ."*`"`'*, ~._, ._ Em
.;,..`,__zj_r
3 _ tzz..->1. z vv
z ...
onreatei)
de volta para a
*f A mw' 'ty
EXQCUBDO j a executar
. g;P31z53()
,. __ A a executar
`
KOJJIHS
A activity visitar?
A activity no est mais 1/SIG
onDestroy{)
Seguindo esse mesmo princpio, tambm o ciclo de vida dos fragments contm esses
mesmos mtodos, os quais so amarrados com a activity que declarou o fragment,
conhecida como host activity. Portanto, quando algum mtodo do ciclo de vida de
uma activity for chamado, como por exemplo, o onPause(), o sistema vai chamar Q
mtodo onPause() em todos os fragments dessa activity. Da mesma forma, quando
o mtodo onResume() da activity for chamado, o mtodo onResume() de cada fragnent
tambm ser.
A maioria dos mtodos do ciclo de vida dos fragments um espelho dos mtodos
da activity, mas tambm existem outros mtodos especcos que existem somente
nos fragments, conforme podemos visualizar na figura 8.12. No lado esquerdo da
gura, podemos ver a ordem em que cada mtodo do ciclo de vida chamado.
No lado direito da gura, feita uma comparao com os estados de uma activity
Podemos ver que os mtodos onStart(), onResume(), onPause() e onStop() so sim
ples, e quando os mtodos da activity forem chamados, eles tambm sero
chamados nos fragments. Os mtodos onAttach(activity), onCreate(), onCreateView()
e onActivityCreated() so executados durante a criao da activity durante o on
Create(). Por exemplo, quando uma activity informa o seu layout pelo mtodo
setContentView(view), os fragments so inados e criados, ento os mtodos onAttach().
onCreate() e onCreateView() so chamados no fragment. Mas somente quando 11
activity retornar do mtodo onCreate() dela que o mtodo onActivityCreated() do
fragment ser chamado. importante ter conhecimento disso, pois nesse mo
mento o fragment sabe que a inicializao da activity foi realizada gm Sucesso
0 que Significa que OCOS OS fragments do layout tambm foram inicializados 6
tiveram suas views criadas.
"`" Y
l
Usurio pressiona
' v"' I
*a 1
"P"' S'
Oagmeni i` " `' Y
. . . _ . novamenfepois VE
""" mstopo *I l executada . "' ' f^ ''~'~~~f~ e ea -~-~~z
Y
OD6nd1() gm T
._....__
DWUBSYOYO
l\[|t0____g4___g g Descrio
onAttach(acti.vi.ty) Esse mtodo chamado logo depois de o fragment ser as
sociado com a activity o que acontece assim que a activity
infla o layout do fragment pela tag ou o tragment e
adicionado dinamicamente via Fragmenransacton. Note que
o importante deste mtodo que ele recebe como parmetro
a activity que contm o fragment. Somente depois que esse
mtodo chamado (mas no nele), o mtodo getActivity()
do fragment vai retornar a activity host.
onCreate(bund1e) Esse mtodo chamado apenas uma vez e quando o fragment
est sendo criado. Ele recebe o Bundle que foi salvo durante
o mtodo onSaveInstanceState(state).
264 Google Android - 4 edio
Mtodo --'3FS59.(9"-) -- ~- e
onCreateView(inater,viewgroup,bund1e)
Nesse mtodo, o fragment precisa criar a view que ser
inserida no layout da activity Somente depois de esse
mtodo retornar, possvel chamar o mtodo getView()
que retorna a view que o fragment tem.
onActivityCreated(bundle) Esse mtodo chamado logo aps o onCreate() da
activity ter sido finalizado. Esse pode ser um bom
momento para consultar os web services e buscar 0
contedo necessrio para criar 0 layout.
onDestroyView() Esse mtodo chamado quando a view do fragment
foi removida e no pertence mais ao fragment. Depois
desse evento, o mtodo getView() do fragment vai re.
tornar null..
onDestroy() Chamado para indicar que o fragment no est mais
sendo utilizado e ser destrudo.
onDetach() Oposto do mtodo onAttach(actvity), esse mtodo
chamado quando o fragment foi desassociado da
activity Depois desse evento, o mtodo getActivity()
do fragment vai retornar null.
_ i
voce precisa entender como reaproveitar o cdigo entre a verso smartphone e
tablet, conforme a figura 8.13.
y. . ou
Nrvllvh Acuvny a I _ _ A com dos
Figura 8.13 - F
mgment que divide a tela em pedaos.
Captulo 8 n Fragments 265
No prximo exemplo, vamos criar uma lista com os nomes dos planetas e ao
selecionar um item da lista vamos navegar para outra activity que vai mostrar os
detalhes do planeta. O layout ser extremamente simples, pois estamos interessa
dos apenas em estudar os fragments. A gura 8:14 mostra o exemplo funcionando.
Observe que na segunda tela o nome do planeta mostrado no ttulo da action bar.
, . Terra
Mercuro
\____/'
Ci>
VGIUS
MBR!
Jptiet
Saturno
P|anetasFragment.java
public class PlanetasFragnent extends android.support.v4.app.Fragment{
@0verride
public View onCreateView(Layoutlnater inater, @Nullable Viewroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_planetas, container, false);
266 Google Android - 4' edio
// LiStView
Listview listview = (ListView) view.ndViewBy 1d(R.id.listview);
listView.setAdapter(new PlanetaAdapter(getActivity(ll);
listview.set0nItemClickListener(onItemClickPlaneta());
return view;
}
};
}
/res/layout/fragment_pIanetas.m|
<?nl version="1.9" encoding="utf~8"?
<LinearLayout nlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="math Parent"
android:orientation="vertical" android:padding="15dp" > _
MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
// 0 cdigo-fonte da activity c vazio.
setContentView(R.Iayout.activity_main);
}
/res/layout/activity_main.xml
<?xm1 version="1.0" encoding="utf-8"?>
<Re1ativeLayout m1ns:android="http://schemas.android.com/apk/res/android"
mins:too1s="http://schemas.android.com/tools"
android:1ayout_width="match_parent" android:1ayout_height="match_parent">
<fragment
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
c1ass="br.con.1ivroandroid.planetas.P1anetasFragment"
tools:1ayout="@1ayout/fragment_planetas" />
if PIanetaFragment.}ava
public class Pl88FfQRt extends Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, VBNGFOUP C"t"r
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_Pl6. fif false);
return view;
l
public void setPlaneta(String planeta) {
Textvieu text = (Textview) getView().ndViewBy1d(R-ld-)
text.setText("Planeta: + planeta);
l
1
fi) /res/layout/fragment_pIaneta.xmI
<?xnl version='1.6' encoding='utf-8?>
<FraneLayout xnlns:android='http://schenas.android.con/apk/res/android'
xnlns:tools='http://schenas.android.con/tools'
android:layout_width=natch_parent android:layout_height='natch_parent'
tools:context='br.livroandroid.livroandroidcap8_planetas.PlanetaFragnent'
<TextVieu android:id='@+id/text
android:layout_width='wrap_content' android:layout_height='wrap_content'
android:layout_gravity='center' />
) PIanetaActivity.java
public class Planetanctivity extends AppConpatActivity {
@0verride
/res/layout/activity_pIaneta.xmI
<?m1 version="1.0" encoding="utf-8"?>
<Re1ativeLayout m1ns:android="http://schenas.android.con/apk/res/android"
xnins:too1s="http://schenas.android.con/tools"
android:1ayout_width="match_parent" android:iayout_height="match_parent"
android:padding="16dp" >
fragnent android:id="@+id/P1anetaFragment"
android:1ayout_width="match_parent" android:iayout_height="match_parent"
c1ass="br.com.1ivroandroid.planetas.P1anetaFragment"
tools:iayout="@1ayout/fragnent_pianeta" />
Veja que o segredo muitas vezes ao utilizar uma activity com fragments como
passar parmetros da activity para o fragment. Como a navegao de telas feita
no nvel da activity e os parmetros so passados pela intent, a activity precisa
ler esses parmetros e atualizar o fragment.
Depois dessas alteraes, o projeto deve continuar funcionando normalmente.
Assim, somente prossiga com a leitura caso seu exemplo esteja funcionando. Se
precisar, confira o exerccio pronto no projeto de exemplo Planetas-Fragnents.
Agora vamos brincar um pouco com fragments e implementar esse exerccio de
outra maneira. No cdigo que zemos at o momento, tivemos de passar um
parmetro para o fragment, e, como esse fragment est inserido de forma esttica
no layout XML, necessrio recuperar o fragment pelo id e chamar um mtodo
setP1aneta(p1aneta) para atualizar o contedo.
Outra abordagem, que eu at prero, adicionar o fragment dinamicamente
pela API. Ento para comear, altere o layout da activity do planeta e remova a
tag . Vamos deixar apenas um FraneLayout com um identificador para
adicionar o fragment.
/res/layout/activity_pIaneta.xmI
<?xn1 version="1.0" encoding="utf-8"?>
<ReiativeLayout . . .
android:id="@+id/1ayoutFrag" >
270 Google Android - 4 edio
, ,. -- ~ ment dinamicame
ossvel assar
O prximo passo e alterar a activity para adicionar o frag nte
no layout e pelo metodo setArguments(bundle) do fragment p P og
assar o mesmo Bundle que a activiti
parmetros. Note que a mgica est em p
recebeu pela intent.
PIanetaActivity.java
PIanetaFragment.java
public class PlanetaFragnent extends Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,Bundle
savedInstanceState) {
View view = inater.inate(R.layout.fragnent_planeta, container, false);
if(getArguments() != null) { \
String planeta = getArgunents().getString("planeta");
setPlaneta(planeta);
}
return view;
}
@0verride
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getArgunents() != null) {
String planeta = getArgunents().getString("planeta");
setPlaneta(planeta);
}
}
public void setPlaneta(String planeta) { .
Textview text = (Textview) getView().ndViewById(R.id.text);
text.setTet("Planeta: " + planeta);
l
}
. '
' a
_ i a t. z ea
Captulo 8 n Fragmemg
_n layout;
p c . i _ _~
p * zz- E
ft. mem
f dargelat1diactvty_mM,,;
wizwm@wt
z;3(.,;,,,,
alizado a seguir:
3 I Tm l
. .; ' * " c _. ""f`
H F* ldravvable-mdp hbleow
1 V llayout l L_lGridLayout
_' activity__main.xmI i Relativeuyout
* activity _planeta.xrnI C3 WMQS
9 .v
1
l
1
l * ?~*@@M#
Y%iz;zi
jmgnu
6dapter_pIneta.xm
fragment_pIant.mI
fr! rnent hnetasxml
P Small Button i
A *~ Bu
13RadoButton
|33v.zu.,-,,,gz0d, [ChecI<Bc|
AndroidManfest.xml H Swllh i
f E1 -szs=
I .V _tI9ss!sHsfrf=
androd:orentaton="horzonta1"
* r
L h:^--
- ~1._.,f~~~~-~.t
,_
' : ]]::;m`dTest E; ~- z _! ta' l:1N=10' i..})' @0Dmtme m- ). zq.
. V' Chuva ElFrameLayout \ ll W l .
~ Eb;_|vmnd,,d_| m,d p8_pmet ' {:|Lin:arLayout (Horizontal
V res ` E]LinearLayout (Vertical)
P Edrawable-hdpi l:lTabI:Layout
r! mzin TetV|ew
-mal Large Text
l L Medium Text
Small Tzzz
/res/layout-xlarge-Iand/activity_main.xmI
<?xm1 version="1.0" encodng="utf-8"?>
<LnearLayout xmlns:androtd="http://schemas.androd.com/apk/res/androd"
xmlns:tools="http://schemas.androtd.com/tools"
androd:layout_wdth="match_parent" androd:1ayout_height="match_parent"
fragment androtd:td:"@+d/PlanetasFragnent"
androd:layout_wtdth="Gdp" androd:1ayout_weight="1"
androd:1ayout_hetght="match_parent"
c1ass="br.com.Ivroandroid.planetas.P1anetasFragment
tools:1ayout="@1ayout/fragment_p1anetas" />
<fragnent android:td="@+td/P1anetaFragment"
androtd:layout_wdth="0dp" androd:1ayout_weght="1"
androd:1ayout_heght="match_parent"
cIass="br.com.1vroandrotd.planetas.P1anetaFragment"
tools:layout:"@1ayout/fragment_p1aneta" />
</LnearLayout>
PIanetasFragment.java
public class PlanetasFragnent extends android.support.v4.app.Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) [
};
}
}
Captulo 8 1 Fragments 275
Execute o projeto no emulador com um tablet de 1O e confira o resultado. Gos
to muito de utilizar o emulador do Genymotion (genymotioncom), pois leve e
rpido. No Genymotion, voc pode criar facilmente o emulador do Nexus 10 e
testar esse exemplo.
Find Fragi By id
Add Frag2
' HE
Remover Frag2
Repiace Frag
Start Activity
f,. =
Na parte dc cima loi inserido o Fragmentl de lorma estattca, L,21 pldflh. C 'diko
licou vazia, pois esse sera espao em que os outros lrmcms fall 'H<Js.
l ,,s
substitudos ou removidos dinamicamente./\ figura 8.17 mstra 21 Pfc'V'5U3l'Z<5<
d arquivo /res/layout/ac'tivity_main.xmI.
i: gq
,_ Rllvlly
W .munrml 1
~* . .
l Io; Liv _~;M!lu4' ' ;' `|'99"'"
1
l tz llll-dll
lb
l. fi
l
.., _. t.
l
Depois dessa breve introduo, recomendo que voc execute esse projeto no emu
lador e brinque com cada opo do menu, pois so todas simples. Na sequencia,
estude o cdigo-fonte da activity para entender o que faz cada opo, mas basi
camente elas demonstram como utilizar a classe Fragmenransacton.
Uma vez que voc se familiarizou com o exemplo, vou dar algumas dicas do qUC
acho importante voc saber. Peo que execute as aes exatamente nesta ordem
que vou lhe dizer e conra os resultads.
Cenrio 1
Cenrio 5
Clique em Add Frag2.
Esse exemplo demonstra de forma simples como iniciar uma activity que tem
um fragment no seu layout. E ainda como passar parmetros para o fragment.
Clique no boto Voltar para voltar tela anterior.
Cenrio 8
Clique no boto ReplaceFrag3.
Note que apareceu uma ao na action bar, com a gura de uma carinha/smile
Isso signica que fragments podem adicionar aes na action bar.
Ao clicar na ao, o prprio fragment faz o tratamento do evento.
Nos prximos tpicos, vamos revisar alguns conceitos importantes que vimos
nos exemplos desse projeto, como a back stack e como os fragments adicionam
aes na action bar.
Se o fragment informar ao sistema que ele deseja adicionar aes na action bar, o
mtodo onCreateOptionsMenu(menu,inater) ser chamado. Basta inflar o XML de menu.
A seguir podemos visualizar o cdigo-fonte da classe Fragnent3 do exemplo anterior.
3
Fragment3.java
@0verride _ _
public class Fragnent3 extends android.support.v4.app.Fragnent {
@0verride
P ublc void 0nCreate0ptionsHenu(Menu menu, Henulnater inflater) {
280 Google Android - 4a Edio
inater.inate(R.nenu.nenu_frag3, menu);
}
@0verride
public boolean onOptionsItemSe1ected(HenuItem item) {
int id = item.getItemId();
if (id == R.id.action_he11o_frag3) {
Toast.nakeText(getActivity(),"Hello ActionBar Frag 3",Toast.LENGTH_SHORT).Show(M
return true;
}
return super.on0ptionsItenSe1ected(item);
}
) /res/menu/menu_fragment3.java
<menu xn1ns:android="http://schenas.android.con/apk/res/android"
m1ns:too1s="http://schenas.android.com/tools"
xm1ns:app="http://schenas.android.con/apk/res-auto" >
<item android:id:"@+d/action_he11o_frag3"
android:title:"@string/he1io_frag3" android:icon="@drawab1e/smilel"
app:showAsAction="a1ways" />
A gura 8.18 mostra o boto com o cone de smile, o qual foi adicionado pelo fragmem.
_.,1
l7f'~`u l .)i]i
OK
Heilo Activity
F Q e- rw
}.
guir, podemos visualizar o cdigo simplicado desse fragment, no qual podemos
Ver como salvar 0 estado da varivel count, a fim de preservar o estado durante a
troca de orientao da tela.
Fragmentl .java
public class Fragmentl extends android.support.v4.app.Fragment {
private int count;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_1, container, false);
// Recupera o estado da varivel
if(savedInstanceState != null) {
count = savedInstanceState.getInt("count");
count++; _
view.ndViewById(R.id.bt0k).set0nClickListener(new 0nClickListener() {
@0verride
public void onClick(View v) {
}) ;
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outSta'C2); d
outState.putInt("count",c0Unt)5 / Salva esta
}
}
, . ndle no '
282 Google Android - 4 edio
(,omo vimos, para salvar o estado, basta preencher o Buf metodo
onSaveInstanceState(bundle), que chamado antes de destruir O fgmem- Dpoig,
ao recriar a view do fragment, basta ler os dados desse Bundle: Dependendo dos
objetos e variveis do fragmer1t,pOdmOS decidir 55 C necessario mlclallzaf 8 tela
ou no. Por exemplo, podemos decidir se nece ssrio buscar os dados do web
service ou no.
Fragment1.java
public class Fragnentl extends android.support.v4.app.Fragment {
private int count;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_1, container, false);
// Indica que este fragnent deve preservar seu estado
setRetainInstance(true);
view.ndViewById(R.id.bt0k).set0nClickListener(new 0nClickListener() {
@0verride
public void onClick(View v) {
count++;
http://developer android.com/guide/componemts/fragmcnts.html
Android API Guides - Supporting Tablets and Handsets
Animaes
H
.
Esse tipo de animao feito com um arquivo XML na pasta /res/drawable, basta
congurar a lista de guras.
/res/drawable/Iist_Ioading.xmI
<?m1 verson="1.0" encoding="utf-8"?
<anmation-list xmlns : android="http: / / schemas . and rod . com/ apk/ res / androtd
android:oneshot="fa1se" >
Mtodo Descrio _g
temos alguns dos principais mtodos da classe Animation.
Por ultimo, depois que a animao foi criada, basta chamar o mtod0
view.startAnimation(animation) para iniciar a animao
liiimf AnImarcomAPI
Rotate
Scale
i
"Translate
i.
AnimationSet l
i; _d
, -_ i ~ ,_ _ ,
__A__
Animationstener 1
i. .. _. -~~f~~~---f-*~-^
E
9.4 AIphaAnimation
. . ' ' ~~ >scondei' ii view
A classe A1phaAnmation perm ite criar os famosos efeitos de fade_in e fade_out. 1721111
^ ' da viewcom
alterar a transparencld 0 obietivo de mostrar ou ea
288 Google Android - 4 edio
Na prtica, a classe AlphaAnination altera a propriedade alpha da view FSPOSvcI
pela transparncia. Sempre que o alpha for 0.0 (zero), Slgmca que '3 VICW est
invisvel, e sempre que o alpha for 1.0 ela est 100% visvel. A propriedade alpha
um oat que varia entre 0.0 e 1.0. A tabela a seguir resume os dois parametros
utilizados pela classe AlphaAnination.
/res/anim/fade_in.xmI
<?xml version="1.0" encoding="utf-8"?>
<alpha xnlns:android="http://schenas.android.con/apk/res/android"
android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="2000" /
/res/anim/fade_out.xmI
<?xnl version="1.0" encoding="utf-8"?>
<alpha xnlns:android="http://schenas.android.con/apk/res/android"
android:fromAlpha="1.0" android:toAlpha="0.9" andrQid;durati0n="z9en" /
Uma vez que os recursos de animao foram criados, podemos utilizar a classe
Aninationutils para carregar e iniciar a animao. Nesse momento podemos utilizar
3 Classe R do 110550 pI`OpI'lO pI'Oj[O para 3CSS8I` SSS I'CLlI'SOS.
View view = /* qualquer view */
boolean show = false; // Mostrar ou no a viewv
int anin = show ? R.anin.fade_in : R.anin.fade_out'
.O
Captulo 9 1 Animaes 289
9.5 RotateAnimation
A classe RotateAnimation serve para para rotacionar uma vievig na qual so utilizadas
as coordenadas x e y e um ngulo em graus para a rotao. Para essa animao;
c possivel configurar um ponto que ser o eixo da rotao. O padro da rotao
a partir do canto superior esquerdo da vievig que tem os pontos x = O e y = O.
A tabela a seguir resume os dois parmetros utilizados pela classe RotateAnimation:
Propriedade Descrio
oat pivotXVa1ue Valor no eixo x para servir de eixo da rotao. Pode receber um
valor absoluto em pixels, em que O (zero) representa o canto
esquerdo superior ou valores relativos coordenada da view ou
de seu layout pai.
int pivotYType Idem a propriedade pivotXType mas para o eixo y
oat pivotYVa1ue Idem a propriedade pivotXVa1ue mas para o eixo ya
O cdigo a seguir demonstra como rotacionar uma view em 1800 graus com a
classe RotateAnimation. A primeira animao gira a vievig e a segunda gira novamente
para a posio de origem.
view view = /* qualquer view */
boolean primeiravez = true;
int angulo = 189; // Girar 180 QFHUS
Animation giralda = new RotateAnimation(0,angu1o,
Animation.RELATIVE_T0_SELF, 0.5F,
Animation.RELATIVE_T0_SELF, 0.5F)5
Animation giraRetorno = new RotateAnimation(an9Ul0,0,
Animation.RELATIVE_T0_SELF, 9.5F,
290 Google Android - 4= edio
Animation.RELATIVE_T0_SELF. 0.SF);
Animation a = prmeiravez ? giralda : gtraRetorno;
a.setDuration(2000); // 2 segundos .
a.setF11After(true); // Manter o efeito no nal da antma
view.startAnination(a);
/res/anim/rotate_gira_ponta_cabeca.xmI
<?n1 verson="1.0" encoding="utf-8"?
!-- Rotao do ngulo 0 para 180 graus. -->
<rotate xmlns:androd="http://schenas.androd.com/apk/res/androd"
androd:fronDegrees="0" androd:toDegrees="189"
androd:pvotX="S0%" androd:pvotY="S0%
androd:duraton="500@ androd:11Aftef="true" /,
/res/anim/rotate_gira_ponta_tabeta_retorno.xmI
<?n1 version="1.0" encoding="utf-8"?>
<rotate xm1ns:androd="http://schenas,andr0 d.com/apk/res/android"
androtd:fronDegrees="180" android:toDegrees="0"
android:pivotX="5G%" android:pivotY=59%"
android:duraton="500B" androd:I1After="trUe" /,
(aptulo 9 z Animaes
ifa1 ' ~ ii
R. anin. rotate_gira_ponta_cabeca_retor`no);
1 al
' An " ' . t l . -- a , l
q Imar com XML q Animal com API T .ggfnzy mm zm, Amma, com Am
*^~ f'~--~~~ ~ f~-~~ ~-~~~ -- --_-~. __;~z z.. ..._ , ....,.. _ 1 '_.H_,,,M___;(_,_%~J_(__M_ _ 'A:_=`M___~._M
il
9 6 ScaIeAnimation
\ classe Sca1eAnimation permite aplicar animaes de escala, para diminuir ou
1ilI11CI1t8I` uma vievi: O exemplo de cdigo a seguir faz a view diminuir graditi
\ imente at ela desaparecer. Na prxima vez, vamos fazer com que ela aumente
dt novo, gradativamente, at o seu tamanho normal.
292 Google Android - 4 edio
View view = /* qualquer view */
boolean primeiravez = true; // 59 ff 3 Primeira vez, vai diminuir...
int angulo = 180;
ScaleAnimation encolher = new SClAt0(
1.0f, G.0f, // X inicial e nal
1.0f, 0.0f, // Y inicial e nal
Animation.RELATIVE_T0_SELF, 0.5f, // Eixo X
Animation.RELATIVE_TO_SELF, 0.5f // Eixo Y
);
ScaleAnimation aumentar = new ScaleAnimation(
0.0f, 1.0f, // X inicial e nal
0.0f, 1.0f, // Y inicial e nal
Animation.RELATIVE_T0_SELF, 0.Sf, // Eixo X
Animation.RELATIVE_T0_SELF, 0.5f I/ Eixo Y
);
Animation a = primeiravez ? encolher : aumentar;
a.setDuration(2000); // 2 segundos
a.setFillAfter(true); // Manter o efeito no nal da animao
view.startAnimation(a);
Assim como as outras animaes, podemos fazer o mesmo efeito com recurso
XML, o que em minha opinio bem mais simples.
O exemplo a seguir dene que as coordenadas iniciais da animao so 1.0 (100%
tanto de X quanto de Y, informando o tamanho total da imagem. E as coordena
das nais informadas so 0.0 (0%), informando que o tamanho nal da imagem
deve ser reduzido em nada. Como 0 eixo da animao foi denido como 5090.1
imagem vai diminuir aos poucos at desaparecer no seu centro.
i /res/anim/scaIe_dimnuir.xmI
Ii
android:l1After="true" androd:duraton="20G0" />
, Animar com XML , 1 Animar com API i L Anlmar com XML /mimar esta wi
LNB* __ vi ,`_ _ ____W_W*__M_4 I l _,____,_ _, _,_ K __W
^aasaa
' ..
tgl l~ z *
l
L >~;.`5*;;,'; z.Ea.{
. . .
._ ;
i i
~
l
l
E
1E
il
9.7 TransIateAnimation
z\ classe TransiateAnmation serve para mover urna view pelo layOL1f ffspeclcanfio
35 goordenadas X e Y iniciais e nais da animao. Essas coordenadas podem ser
passadas como um valor absoluto ABSOLUTE em pixels de tela, ou relativo a propi ia
view RELAT1vE_To_sELF ou ao seu layoutpa1RELATIVE_T0_P^RENT
. . " . ~ z ~- ~> ateAnimation.
A mbkh il Scguif fgmn os parametros utilizados pela classe Transi
I GOOQIQHIIOU-QIQQM
Propriedade DdCfI
non fmnnym Tipo da coordenada x inicial.
float fromtvaloe Valor da coordenada x inicial.
'L-nt toXTwe Tipo da coordenada x nal.
float t~oVa1ue Valor da eoordenacln x nal.
int front/Type Idem explicado no eixo x.
float front/Value Idem eqIiendo no -eixo x.
int toi/Type Idem explieado no eixo x.
float toYValue Idem explicado no eixo X.
Biisicainente. n elnsse recebe as coordenadas X e Y iniciais c nais. 'lemos assim
quatro pontos. Para cndn ponto, e especificado qual o tipo do valor, que podf
ser Anlnatton.ABSOLUTE. Animation.RELATIVE_T0_SELF ou Animation.RELATIVE_T0_PARENT_
Sempre que o tipo de um valor 'for Antnatton.ABSOLUTE, aquele parmetro poder
recelwer valores alwso-luros. que so valores numricos que especieam a real
posio ou y em pixels. Se o tipo do parmetro for Antnatlon.RELATIVE_T0_SELF ou
Antnatton.RELATIvE_T0_PARENT, os valores informados sero em percentual. No XMI
so utilizados os valores 0% e 100%. e nojava so utilizados os valores ODF e IDF.
O trecho de codigo n seguir mostra como mover a .imagem para baixo e depois
para eimn. O valor 2 neste exemplo 6 para a imagem se mover em duas veres n
seu tamanho
View vtew - I* qualquer view */
boolean prtnetravez = true; /I Se for a prtnetra vez, nover para baixo.
Antnatton-noverParaBato = new TranslateAnnatton(
Animation.RELATIvE_T0_SELF, 8.0F. Antnatton.RELATIVE_T0_SELF, 0.0F,
Anlnatlon.RELATIVE_T0_SELF, 0.8F, Animation.RELATIVE_T0_SELF, 2.0F
):
Animation noverParaCtna = new Trans1ateAntnation(
Antnatton.RELATIV_T0_SELF, 0.0F, Antnatton.RELATIVE_T0_SELF, B.6F,
Antnatlon.RELATIVE_T0SELF, 2.0F, Antnation.RELATIVE_T0_SELF, 9.6F
):
E a animao a seguir o contrrio, faz com que a imagem volte para cima.
() /res/anim/transIate_mover_para_cima.xml
<?m1 verson="1.0" encodng="utf-8"?>
<trans1ate xmlns:androd="http://schemas.androd.com/apk/res/androd"
android:fromXDe1ta="@.0" androd:toXDe1ta="@.0"
l
android:fromYDe1ta="200%" androd:toYDe1ta="@.0"
android:11After="true" androd:duraton="2000" />
Ao executar esse exemplo, a imagem vai mover para baixo numa distncia de duas
i. I1g 11 _
vezes o seu tamanho, e depois na prxima vez a imagem vai se mover para cima
at voltar sua posio original. A gura 9.4 mostra o resultado dessa animao.
(___ _
ML
i AnimarAlmf
com(Om
X _P
1 Wgmwg _``, __,\__,A_,___= W; ~_,_.\__,,.--_-._a._._...~... ; ~ ~ -fr f ^" 's
^ - e _9.4
ZIHZLI ~ -dti ozs
em '- t - ~ ' /mito
Figura Extmplo d 3 P dt st mova pam . .
296 Google Android ~ 46 adm
9.8 AnimationSet
'ma es para serem executad
A classe AnimationSet permite agrupar vrias am . b_ HS ao
mesmo tempo. A maneira de unliz-la simples: basta criar um o jeto Antmattonsez
e adicionar as animaes que devem ser executadas.
A classe AnimationSet contm ainda os mtodos para configurar as propriedades das
animaes, como o mtodo setDuration(1ong),o qual altera 0 tempo de execuo Se
alguma propriedade for alterada nessa classe, ela vai sobrescrever as propriedades
que foram antes conguradas em cada animao que foi adicionada na lista. Para
facilitar o entendimento, o cdigo-fonte a seguir mostra como mover uma imagem
ao mesmo tempo em que ela vai cando transparente, com a unio das classeg
Trans1ateAnimationt:A1phaAnimation.
/res/anim/set_mover_para_baixo_desapareter.xmI
<?m1 version="1.0" encoding="utf-8"?>
<set xmins:android="http://schemas.android.com/apk/res/android">
<trans1ate
android:fromXDe1ta="0.0" android:toXDe1ta="0.0"
android:fromYDeita="0.0" android:toYDe1ta="200%"
android:l1After="true" android:duration="2000" />
<a1pha
J za `*; . 1 "L;*r'
ia*
l . .>_zze_.=._x>._,,__ ..\_: _:.~..
Wi : 1. - ;~'* 1 i
, ,_
l
rm
u. 5
@0verride
public void onAnimationEnd(Animation animation) {
// A animao terminou
}
@0verride
public void onAnimationRepeat(Animation animation) {
// A animao est repetindo
}
});
9.10 Interpolator
Imagine que voc possui uma animao que vai mover o objeto da esquerda para
direita com o tempo de durao de dez segundos. De certa forma, podemos dizer
que, se dividirmos a distncia que ser percorrida pelo objeto por 10, o objeto vai
a cada segundo mover 10% da distncia. Se a distncia total a ser percorrida lior
100px, o objeto ser movido 1Opx por segundo. Esse o esperado e o compor
tamento padro, mas possvel alterar a acelerao dessa animao e customizar
seu comportamento.
d. . z . ` _ '
Quem dee esta taxa "rate" da animao a interface Interpolator. Como padr0~
em um carro l ' - . , .
ao sem COUSISWUYC C ter O mesmo efeito durante todo o tempo Agora vws
12f que 0 0bJet1v0 Seja acelerar o objeto que est se movendo Se pensarm05
E C Cma dfagaf ate ganhar aceleraao, e depois que embala \~
embora. Mas lembre-se: muita calma nessa hora Podemos criar o mesmo efew
CaptuIo9 n Animaes 299
com a animao de um objeto e aceler-la aos poucos. Usamos para isso a classe
Acceieratelnterpoiator. Para informar qual interpolator deve ser selecionado, chame
o mtodo setInterpol.ator(nterpolator) da classe Animation;
Anzimation anim = ;
ani.m.setInterpo1ator(new Acce1erateInterpolator());
6- Anim
lmf i
.` I
\
\
i/
j nlmf
/res/anim/animator_1_para_0.xmI
<aninator xmlns : androd="http : //schemas . android . com/apk/res/android"
android:duration="1000"
android:va1ueFrom="1" android:va1ueTo="0"
android:va1ueType="oatType" android:repeatCount="1" android:repeatMode="reverse" />
_ . . . ` ` ` ilar
Verificamos que essa animao dene que o valor inicial 1 e o nal O, mas no
dene qual a propriedade ou tipo da animaao que sera real1zado.|Isso acontece
porque o conceito de animaes nesse novo framework e generico. Podemos
informar o valor inicial e nal de forma abstrata, para posteriormente vinci
esses valores com alguma propriedd f211~
Uma caracterstica importante que podemos vericar nesse recurso de animao
esse 1interva
l lhido assar deo,
1 a Oo(note
vaqueor esco P'
que o tempo da animao foi denido como um segundo, e, portanto, durante
os nmeros so float)
302 AIIO - 43
~ ,- -. -. mos recu erar es - t
. _ . - ~ ~ 5 umte forma:
Uma vez que o recurso de animaao esteja CI'l1Cl0 Pode _ P Sa &m~
mao e instanciar um objeto do tipo ValueAmnator da seg
. _ _ _ - irias ri* .
V1u@AninatQr 3 z (va1ueAninator) Aninatorlnater.loadAninator(\h15
R.anim. aninator_1_paf3_9);
});
Significa que os mtodos getAlpha() e setAlpha(oat) existem na classe View. Note que
o mtodo offFloat indica que o tipo do parmetro oat. Seguindo o mesmo prin
cpio, sabemos que podemos alterar a propriedade , porque existem os mtodo
304 Google Android - 4 @d
.~'"md'z
getX() e set(oat). dessa maneira que a classe 0bCt^"3tf funciona, podendo
alterar qualquer propriedade de um objeto durante a HDIITIHGO 6 Ctrminzdo
intervalo de tempo. um conceito bem flexvel.
A lista a seguir exibe as propriedades mais comuns que podemos alterar com
essa classe, para obter as tradicionais animaes de alpha, rotate, scale, translate_
alpha
xey
Coordenadas com a posio da view
translatonX e translatonY
pvotX e plvotv
AIphaAnim.java
public class AlphaAnim extends Activity {
private boolean visivel = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.eemplo_animacao);
}
}
306 Google Android -vmjm
criar a animao
Esse exemplo utiliza a classe 0bjectAntnator parapela
' . API r
v 'Dor
XML. Para criar o mesmo cfeito em XML, l121SlH Cflf Um arquivo QIUC Cmec
com a tag c configurar as prpriCd8d5
i /res/anim/fade_out.mI
<objectAninator xmlns:androld="http://schenas.androd.com/apk/FGS/BHTO"
android:propertyName="alpha"
android:valueFrom="1" android:valueTo="G"
android:duration="1000" android:valueType="oatType" />
Nota: o mtodo reverse() reverte a animao tornando o cdigo bem mais simples.
Nos casos em que voc precise programar vrias animaes em conjunto (ex:
alpha + translate + rotate) o mtodo reverse() realmente vale a pena, pois economiza
vrias linhas de cdigo.
/res/anim/rotate_360.xmi
<objectAnimator xmlns :-android= "httpz / / schemas ,android . com/ pk/ res / android"
android:propertyNamQ="FOQOD"
android:-vaiueFrom="Q" andr0id:_\a1_ueTo="360"
android:dura_tion="1000" android:_va1ueType="oatType" />
Nota: no framework View Animation, vimos que o cdigo necessrio para animar
uma view muitas vezes era extenso e justamente por isso era recomendado
criar a animao em XML para facilitar a leitura do cdigo. Com o framework
Property Animation o cdigo se tornou extremamente amigvel, e podemos criar
animaes com apenas. uma linha de cdigo., Portanto, ca a seu critrio utilizar
API ou XML.
308 Google Android - 4 um
9.18 Objectllnimator - animao de estala
Ate o momento. tralialliamos corn a classe Objectlminator para alterar uma nica
propriedade de um olijcto para criar a a niniaczio No entanto, as vezes, dependencia
do caso. precisamos alterar mais de uma propricdlslt
Neste pixiino exemplo precisamos alterar duas propriedades do objeto. scalex Q
scaleY Pois P\`cisamos criar o efeito de diminuir o Olllfm "l CIC d*`5ll@f0Cer. e
depois aumenta-lo novamente ate o tamanho normal. Para alterar mais de uma
pnopriedade. e necessario criar um PropertyValuesHolder com as informaes de que
precisamos Essa classe apenas tem o objetivo de armazenar a informaao Salim
uma propriedade para ser animada.
PropertyValuesHolder aninl = Propertyvaluesiiolder.ofFloat("scalex", 1, G);
PropertyvaluesHolder anin2 = PropertyValuesHolder.ofFloat("scaleY, 1, B);
Feito isso, o objeto 0bjectAninator vai executar as duas animaes simultneas para
criar o efeito de escala tanto no eixo x quanto y do objeto.
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueTo="200" android:valueType="oatType"
android:propertyName="x" android:repeatCount="1" /
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueTo=400" android:valueType="oatType"
android:propertyName="y" android:repeatCount="1" />
9.20 Animatorlistener
Com o framework Property Animation tambm podemos monitorar a execuo das
animaes, basta implementar a interface and roid . animation .Animator.AnimatorListener:
Animator anim = m;
anim.addListener(new Animator.AnimatorListener() {
@0verride
public void onAnimationStart(Animator animation) { }
@0verride
public void onAnimationEnd(Animator animation) { }
@0verride
public void onAnimationCancel(Animator animation) { }
@0verride
public void onAnimationRepeat(Animator animation) { }
});
}>;
Com a nova sintaxe, basta uma linha de cdigo e a animao est feita:
myView.animate() .x(S9f) .y(166f) .a1pha(0);
Como podemos fazer esse tipo de animao? A classe ValueAnimator pode ajudar,
basta denir o intervalo numrico que voc deseja e que o framework envie as
atualizaes e o tempo da animao. O seguinte trecho de cdigo mostra como atu
alizar o texto de um Textview do valor 0 para 100 de forma incremental e animada,
durante o intervalo de um segundo. Voc poder ver os nmeros incrementando
at chegar no valor nal.
nal TetView textview = ?
// Animaco genrica de 1 at 100
ValueAnimator a = ValueAnimator.ofFloat(1, 100);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAninationUpdate(ValueAnimator animation) {
// Recebe o valor e atualiza o texto.
Float valor = (Float) animation.getAninatedValue();
tetView.setText(String.value0f(valor.intValue()));
}
});
a.setDuration(1000);
a.setTarget(tetView);
a.start();
/res/anim/animatao_Iayout_fade_n.xml
<?m1 verson="1.0" encoding="utf-8"?>
<layoutAnnaton xnlns:androd="http://schenas.androd.com/apk/res/androtd
android:annaton="@anm/fade_n"
android:duraton="50" />
Pronto, agora como um passe de mgica sempre que voc adicionar ou remover
uma view desse layout o Android vai animar essa transio.
makeScaieUpAnimation(View source, int startX, int startY, int width, int height)
Congura uma animao customizada de escala para a activity que vai
ser chamada. As coordenadas startX e startY so as posies para iniciar a
animao, referentes view Os parmetros width e heigth denem o tama
nho inicial da nova activity
) MainActivity.java
public class MainActivity extends AppConpatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
No cdigo foi denida a anima fade_in (aparecer) para a activity que vai abrir
e fade_out (desaparecer) para a activity que vai fechar, portanto crie os seguintes
arquivos de animao na pasta /res/anim.
i /res/anim/fade_in.xmI
<?m1 verSi0n="1.0" encoding="utf-8"?
<alpha xmlns:android="http://schenas.android.com/apk/res/androdn
android:fronAlpha="0.0" android:toAlpha="1_@" ndr0dduratn:190e />
Captulo 9 I Animaes 315
/res/anim/fade_out.xmI
<?xml version="1.0" encoding="utf-8"?>
<alpha mlns:android="http://schemas.android.con/apk/res/android"
android:fronAlpha="1.0" android:toAlpha="0.0" android:duration="1000" />
Na segunda activity vamos mostrar a mesma gura do planeta, sendo assim, voc
pode at copiar o layout anterior. A diferena que na primeira activity deixei a
guzra pequena com 100dp e na segunda deixei a figura grande ocupando o tamanho
que ela tem com a notao wrap_content. Na segunda activity o detalhe importante
que sobrescrevemos o mtodo finish(), pois preciso customizar a animao de
sada chamando o mtodo overridePendingTransition(animEntrada, animSaida).
PIanetaActivity.java
public class PlanetaActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
getSupportActionBar().setDisplayHoneAsUpEnabled(true);
}
@0verride
public void nish() {
super.nish();
// Customiza a animao ao fechar a activity
overridePendingTransition(R.anim.fade_in, R.anin.fade_out);
}
/res/anim/sIide_in_Ie1't.xmI
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="50@" android:fromXDelta="1@0%p" android:toXDelta="0" />
316 9
0 't O . O oca: 9 4 ' 0 On'
` " ' aaa in t nouonfi* N A N N ,____
HIoAcuvityTnnaition
Google Android - 4 @d
/res/anim/slide_out_Ieft.mI
<trans1ate n1ns:android="http://schenas.android.com/apk/res/android"
android:duration="500" android:fromXDe1ta="0" android:toXDe1ta="-5G%p" />
ifo /res/anim/slide_in_right.xmI
<trans1ate xnins:android="http://schenas.android.com/apk/res/android"
android:duration="S90" android:fr
WD@lta="-50%p" android:toXDelta="0" /
Captulo 9 I Animaes 317
. I '
/res/anim/sIide_out_rght.xm|
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="S00" android:fromXDelta="0" android:toXDelta="100%p" />
r'~.`,.J
Nota: a classe Activityptions foi criada no Android 4.1, mas para facilitar nossa vida
o Google disponibilizou as classes android.support.v4.app.ActivityCompat e android.support.
v4.app.Activity0ptionsCompat para termos a compatibilidade com verses antigas. Veja
que estamos utilizando as classes de compatibilidade no cdigo. Caso a animao
no possa ser feita nas verses antigas do Android, a activity vai abrir normalmente.
318 Google Android -4Q|||
f - > ` ' 'V ~ ` ` .` fr; I
9.26 Nine0IdAndroids - animaes com compotlbllldidf
l,a vai a dica hnal sobre anunaoes! Neste captulo. estudamos o "l11CW()I'l(
Pf0P0fYAnlmatlons que funciona somente no /\ndroid 3.0 (l'l0f`|CYUml7) U SUPCWUY
e facilita bastante a criao de animaes. li o melhor exemPl0 de md fm 0 que
mostramos no topico VIewPtopertyAnImator, pois com urna umca linha de codigo
possvel animar diversas propriedades de uma view.
- - * ` ~" 1 -' f
myView.anmate().(50f).y(100f).a1pha(0);
ii /app/buiId.gradIe
dependencies {
Compile 'com.ntneo1dandrods:library:2.4.0'
}
'A
ANIMAR X, Y E ALPHA ANIMR X, Y E ALPHA
QIIII. _ . ._.
i:ri_;____;}..
,- ai
Property Anlmatlon
http://dei/elopenmdroid,com/guide/topics/graphics/propanimation. html
http;//dgvglopgzgndrgid,mm/gaide/topics/graphics/drawable-animation. html
maior html
http://android-developers.blogspot.com.br/201 1/05/introducing-viewpropertyang
http://nineoldandroids.com/
Vdeo no YouTube - Android DevBytes: Window Animations
10.1 Introduo
Quando um aplicativo aberto no Android, um processo dedicado no sistema ope
racional criado para execut~lo. Cada processo tem uma nica thread, conhecida
como Main Thread ou UI Thread, responsvel por gerenciar todos os eventos da
tela, assim como atualizar a interface grca. O fato que muitas vezes a activity
pode realizar uma tarefa um pouco demorada, e para no travar a interface de
usurio recomendado que esse tipo de cdigo seja executado em outra thread.
Podemos dizer que, sempre que uma consulta realizada em um web service,
banco de dados, agenda de contatos ou leitura de um arquivo, obrigatrio criar
uma thread para desvincular esse processamento da thread principal. Antigamente
isso era apenas uma recomendao, mas nas verses mais novas do Android, se
o cdigo zer uma operao de I/O na thread principal, o sistema vai lanar a
exceo Network0nMainThreadEcepton.
Portanto, como regra, toda operao de I/O, seja consultar um web service, ler
um arquivo ou acessar do banco de dados, deve executar em uma thread separada.
321
322 Google I1d|`0(| - 4' QO
Outro motivo importante para utilizar threads porque a thread principal da
aplicao deve responder aos eventos do usurio, em no mximo em cinco segun
dos. Se esse tempo for ultrapassado, o erro AINR (Application Not Responding)
ser lanado. Esse erro a clssica mensagem com um Forteose que aparece em
muitas aplicaes, porque, nesse caso, o Android entende que a aplicao no est
respondendo e exibe esse alerta para o usurio fecha-la ou aguardar. Para evitar
esse tipo de erro, necessrio utilizar threads.
Uma vez que j justicamos a necessidade de utilizar threads, vamos ver um trecho
de cdigo em Java que executa um cdigo em uma nova thread.
new Thread() {
public void run() {
// Cdigo que deve executar en segundo plano aqui
};
}.start();
Uma thread deve ser lha da classe Thread e deve implementar 0 mtodo run(). Ao
chamar o mtodo start(), a thread iniciada, ou seja, 0 mtodo run() vai executar
em segundo plano. Para mais detalhes sobre threads no java, recomendo uma
leitura adicional em livros sobre essa linguagem.
No caso do Android, sempre que uma thread iniciada, temos um problema,
pois por questes de segurana e concorrncia o Android no permite que uma
thread diferente da principal atualize a interface grca da tela. Por isso, a classe
android.os.Handler foi criada com o objetivo de enviar uma mensagem para a thread
principal, para que, em algum momento apropriado, essa mensagem possa ser
processada de forma segura e consequentemente atualizar a interface grca da
tela (view). Um exemplo clssico de utilizao de threads e atualizao de interface
grca com um Handler pode ser visto a seguir:
nal Handler handler = new Handler;
new Thread() {
public void run() {
// Cdigo que deve executar en segundo plano aqui
handler.post(new Runnable() {
public void run() {
// Cdigo que atualiza a interface aqui
}
});
};
}.start();
Captulo 10 1 Threads, Handler e AsyncTask 323
Ou podemos utilizar o mtodo run0nUIThread(runnable), que um atalho para utilizar
um handler que est dentro da activity
new Thread() {
public void run() {
// Cdigo que deve executar em segundo plano aqui
run0nUiThread(new Runnab1e() {
public void run() {
// Cdigo que atualiza a interface/view aqui
}
});
};
}.start();
1. Atualizar a interface (view) sempre que uma thread for utilizada para fazer
algum processamento em segundo plano.
2. Agendar uma mensagem android.os.Message ou um java.lang.Runnab1e para
executar em determinado momento. Essa mensagem pode ser enviada
instantaneamente ou com um intervalo de tempo (delay). Cada mensagem
enviada processada em uma la de mensagens nica para cada handler,
que est vinculada thread principal da aplicao.
fi /res/layout/activity_demo_handIer_message.xmI
<?xml verslon="1.0 encodlng="utf-8"?
<LlnearLayout mlns:androd="http://schemas.androd.com/apk/res/androld"
androld:layout_wldth="match_parent" androld:layout_helght="match_parent"
androld:orlentaton="vertcal" androd:paddlng="20dp"
<TetVew
androd:layout_width=match_parent" androld:layout_helght="wrap_content"
androd:tet="Dlsparar uma mensagem com atraso de 3 segundos" /
<Button
androld:ld="@+ld/btEnvlar"
BHFO2lY0Ut_width="wrap_content" androld:layout_helght=wrap_content"
androld:text="Envlar mensagem" /
</LlnearLayout
Captulo 10 I Threads, Handler e AsyncTask 325
DemoHandlerMessageActivity.java
});
}
Note que, para utilizar o mtodo sendMessage(msg) ou uma de suas variaes, pre
ciso criar uma subclasse da classe android.os.Handler; justamente por isso, criamos
a classe TesteHandler. Feito isso, para enviar uma mensagem ao handler, foi criado
um objeto do tipo android.os.Message e no atributo what foi informado um valor, que
contm uma constante para identicar a mensagem.
Message msg = new Message();
msg.what = MENSAGEM_TESTE;
handler.sendMessageDelayed(msg, 3000);
326 Google Android - 4 edio
O mtodo sendMessageDe1ayed(msg,de1ayHi11is) recebe a mensagem e o tempo em
milissegundos (delay) para atras-la. Nesse exemplo, depois de trs segundos, 0
mtodo hand1eMessage(message) da classe interna TesteHandler foi chamado. Nesse mo
mento, o valor informado no atributo what utilizado para identicar a mensagem
(isso til caso exista mais de uma), conforme demonstrado a seguir:
public void handleHessage(Message msg) {
// 0 atributo msg.what permite identicar a mensagem
switch (msg.what) {
case MENSAGEM_TESTE: \
Toast.makeTet(Exemp1oHandler.this, "A mensagem chegoul", Toast.LENGTH_SHORT).show();
break;
}
|'"l`iLlf) '
DlSp3I' Uma mf`lSBgl' (Om atraso E 3 V
Em/lar mensagem l
1
Amensagem chegou!
Para praticar como utilizar o mtodo post( . . .) e suas variaes, vamos criar um
exemplo para enviar uma mensagem com atraso (delay).
DemoHandlerMessageActivty.java
}, 3000);
}
});
}
Esse cdigo tem o mesmo objetivo do exemplo anterior, mas note que a sin
taxe utilizando um Runnable mais simples do que enviar uma mensagem pelo
sendMessage(msg). Ao executar 0 cdigo, 0 resultado ser o mesmo da gura 10.1.
Vale lembrar que a interface Runnable uma gurinha bem conhecida no mundo
java e podemos dizer que ela representa algum cdigo que deve ser executado.
Se necessrio, procure mais detalhes sobre essa interface.
Vamos utilizar este cdigo que vai funcionar como exemplo porque apresenta
um bug.
Captulo 10 I Threads, Handler e AsyncTask 329
/res/layout/activity_downIoad_imagem.xm|
<?ml version="1.G" encoding="utf-8"?>
<FrameLayout xmlnszandroid="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<InageView android:id="@+id/img" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" android:scaleType="tCenter" /
<ProgressBar android:id="@+id/progress"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"/
No layout XML existe um Imageview e por cima foi inserido um ProgressBar para
mostrar a animao durante o download da imagem. O cdigo da activity sim
plesmente faz o download de uma imagem e atualiza o contedo no Imageview.
DownloadlmagemActivty.java
public class DownloadImagemActivity extends AppCompatActivity {
private static nal String URL = "http://livroandroid.com.br/imagens/livro01.png";
private ProgressBar progress;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_download_imagem);
progress = (ProgressBar) ndViewById(R.id.progress);
progress.setVisibility(View.VISIBLE);
downloadImagem(URL);
}
// Vai dar Erro neste mtodo pois somente a UI Thread pode atualizar a view
private void atualizaImagem(nal Bitmap imagem) {
// Esconde o progress
progress.setVisibility(View.INVISIBLE);
// Atualiza a imagem
Imageview imgview = (ImageView) ndViewById(R.id.img);
imgview.setImageBitmap(imagem);
}
DownIoad.java
public class Download {
public static Bitmap downloadBitmap(String url) throws IOException {
// Faz o download da imagem
Bitmap bitmap = null;
InputStream in = new URL(url).openStream();
// Converte a InputStream do Java para Bitmap
bitmap = BitmapFactory.decodeStream(in);
in.close();
return bitmap;
}
AndroidManifest.xmI
<manifest . . . />
uses-permission android:name="android.permission.INTERNET" /
<application ... /
Para fazer o download, estamos iniciando corretamente uma thread, pois isso
e obrigatorio. Contudo, no nal do download, ao tentar atualizar o Imageview, o
Android vai lanar uma exceo, conforme mostra a gura 10.2.
5mPf que Um UFO HCOIWCC, voc deve olhar os logs detalhados na janela LogCat,
Conforme 21 figura 103, que mostra a stack trace do erro. Podemos ver que o pr0
blema est na linha 46 da classe que criamos. Para mais detalhes do I.o9(at, reviS
Captulo 10 I Threads, Handler e AsyncTask 331
os captulos 2 e 3. Note que a mensagem de erro : Only the original thread
that created a view hierarchy can touch its views. Traduzindo a mensagem, isso
signica que somente a thread principal (UI Thread) pode atualizar as views!
Portanto, aqui que a classe Handler entra em ao.
ins uma
i ,_ '09-20 10:30:08.124 62-92/2 E/Iuputsparcher channel '40'7a4f8 br.livroand:oid.ca:ro:s/br.1iv2oan:iro1d.ca:zo:..
'09-20 18:30:08.124 G2-92/2 E/Inpucllispezcber channel 'l0'a4f8 br.livxoandzroidazros/br.livrc:and:oid.carros..
* O9-20 54.706 62-92/2 E/Inputzbispaccher channel '4080ledO br.livroandroid.livroandzuidcapi.l_hnndler/br.
z :09-20 54.706 62~92/? E/Input:Diapatcher channel 40801ed0 br.livroandtoid.1ivmandroid:apl1_handler/br.
":-1
._ OQ-20 07.115 62-92/?
09-20 07.115 62-92/? E/Inputbiapatcher
E/Inputbiapatcherchannel
channel. -061ebd0bnlivroandxoid.livrcandxeidcapl1__handle:/br.
406lu!.~d0 br.livmandzoid.livrcandroidcapllghandlerbr.
l' 509-20 48.405 62-92/7 E/Inputbispatcher channel 40762508 br.livraandroid.livtoendrcidcapl1__handler/br.
` 092O 48.405 62-92/? E/InputDispa\:che: channel '407{508 br.livrodndroid.l1vroand.roidcap1l_han:1lerfhr.
09-20 48.425 62-92/7 E/Inpucbispacchez: Received spurious receive callback ter unknown inpuc channel.
i.
l G 09-20 26.435
09-20 62-92/?
26.435 E/Inpucbispetcher
62-92/7 channel
E/Inpucbiapaccher '40769af0
channel bnlivxoandrnid.liv:oand.roldcap1l__hand1er-'br.
'|0'i69a0 bnlivroandroid.livroand.roidcap11_hundle:/br.
09-20 54.337 B82-890/? E/Androidkuntlnel FATAL EXCEYIION: Thread-10
android.v1ew.ViewRoat:$Celledfroimnqlhreedxcepxzianz Only the oriqinul thread that created A view hierucny can
ac andrid.view.VievRrot . chechread (viewkooc . j av: : 2932)
at nnd.roid.viev.Vie'uP.oox: . invalidecethild (Vie\P.cm: . java : 642)
ac andmid. v1e\:.V1evRom: . invalldatethildlnarent (viewkooz _ java : 665)
1 ac andreid.v:\ew.VievGroup. inva1ida\:eCh11d (yzgvrnw. i gva :
. at andreia . view . View . invalidez: (fg ei . >> avg ; 2 3)
^ ac and:oid.viea.Yie'r. aetflaga (View. vga- ; 556)
l na uiazmi-1.v1ew.v1ew. ze:v1z1b111:y3g;z,;,_az;5
* al: androidfidqec.P:oqressBar.aet:V1s1.bili\:yr.~,resg
3 lb b: . livmandroid. livroandro1dcap11_hundler . Downloadlmnqemhccivltyil . run (
__ _
r; DownloadlmagemActivity.java
}.start();
}
}
Captulo 10 I Threads, Handler e AsyncTask 333
Com essa alterao, o exemplo vai executar perfeitamente, pois o download
feito em uma thread separada, mas a view atualizada na thread principal com
a ajuda do nosso amigo Hand1er.A gura 10.4 mostra o resultado.
Q :I ' 3
Download llillflgfflll
I.
Google
.
1
HIIDROII) 1
Apnndaacarnpllcacsparadisposmvosmvels
oomoAndrodSDK `
novatec nrlw-on.|.cmu g 1
DownloadlmagemActivity.java
public class DownloadInagenActivity extends AppConpatActivity {
}.start();
}
@0verride
protected void onDestroy() {
super.onDestroy();
// Cancela o runnable ao sair da activity
handler.renoveCallbacksAndHessages(null);
}
/res/layout/activity_spIash_screen.xml
<FraneLayout nlns:androd="http://schemas.android.con/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent">
<InageView androd:src="@drawable/livro_android"
androd:layout_width="wrap_content" android:layout_heght="wrap_content"
android:layout_gravity="center"/>
O cdigo da activity vai mostrar a tela por um segundo e depois vai prosseguir para
a primeira activity do projeto. Para isso, uma mensagem enviada ao handler com
um atraso de um segundo. No momento em que o handler receber a mensagem,
a aplicao j foi carregada e a prxima activity j pode ser exibida.
SpIashScreenActivity.java
public class SplashScreenActvity extends Activity {
@Overrde
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Exibe o layout com a imagen...
setContentView(R.layout.activity_splash_screen);
// delay de 1 segundo
new Handler().postDelayed(new Runnable() {
336 GO0Ql|! hdlld - 4' edlh
@0verride
public void run() {
// Inicia a HainActivity ,
startActivity(new Intent(getBaseContet(), HainActivity.c1ass));
// Fecha a activity da splash
nish();
}
J AndroidManifest.m|
<nanifest . . .>
<uses-permission android:nane=android.permission.INTERNET" /
<app1ication . . . >
activity androtd:nane=".SpIashScreenActivity"
intent-Iter
<action android:nane="android.intent.action.HAIN" /
category android:nane=android.intent.category.LAUNCHER /
/intent-lter
/acttvity
<activity android:name=".MainActivity" android:1abeI="@string/app_nane" /
por isso que, ao executar o projeto de exemplo deste captulo, voc ver uma
splash screen antes de visualizar a lista com os exemplos. Lembre-se de que a tela
de splash screen deve chamar o mtodo nish() para encerrar (3553 activity para
destruir a splash ao entrar no aplicativo.
10.7 AsyncTask
Criar uma thread simples, mas vimos que necessrio utilizar um Handler ou o
mtodo de atalho run0nUIiThread(runnable) para atualizar a interface.
Porm ao desenvolver aplicativos para Android no utilizamos threads diretamen
te, pois se recomenda utilizar a classe AsyncTask, a qual representa uma pequena
biblioteca de threads. A seguir podemos visualizar as suas principais caractersticas.
1. A classe AsyncTask gerencia internamente as threads e handlers necessrios
para atualizar a interface.
2. Uma tarefa pode ser cancelada se chamar o mtodo cancell(boolean).
3. Contm mtodos para atualizar o andamento (progresso) de uma tarefa,
por exemplo, o progresso de um download.
4. Contm um pool de threads que pode executar as tarefas de modo serial
ou em paralelo.
Para explicar o que uma AsyncTask, vamos ver um exemplo de cdigo. Basicamente
para criar uma tarefa preciso criar uma subclasse de AsyncTask e informar os trs
argumentos <Parans,Progress,Result>, conforme demonstrado a seguir. A sintaxe
<Parans,Progress,Result> das classes genricas (Generics) do Java.
private class DownloadFilesTask extends AsyncTaskURL, Integer, Long> {
@0verride
protected void onPreExecute() {
// Executa na thread principal
// til para mostrar um ProgressDialog ou ProgressBar
}
O cdigo anterior est comentado, ento leia-o com ateno. Depois de criar essa
classe, para execut-la basta chamar o mtodo execute() e informar os parmetros,
que nesse caso pela Genetics foi denido como um objeto do tipo URL.
new DownloadFilesTask().execute(url);
return totalSize;
}
}
Captulo 10 I Threads, Handler e AsyncTask 339
Na prtica, eu nunca precisei utilizar os mtodos pub1shProgress(progress) e
onProgressUpdate(progress), pois geralmente em aplicativos a nica coisa de que
precisamos consultar um web service, e durante isso podemos mostrar uma
animao com um ProgressBar ou Progressalog. Portanto, costumo utilizar um
template apenas com os mtodos onPreExecute(), doInBackground() e onPostExecute().
A lista a seguir explica o signicado de cada mtodo da AsyncTask.
Mtodo Descrio
onPreExecute() Mtodo executado antes de a thread iniciar, sendo uma boa
oportunidade para exibir uma janela de progresso ao usurio
ou uma mensagem de por favor, aguarde? Esse mtodo executa
na UI Thread."
doInBackground() Mtodo executado em background por uma thread, que deve
conter todo _o processamento pesado. Ele pode retornar um
objeto qualquer, o qual ser passado como parmetro para o
mtodo onPostEecute(). aqui que a thread executa, mas isso
feito automaticamente para voc.
onProgressUpdate() Mtodo chamado na UI thread e recebe geralmente um inteiro
para informara quantidade do progresso. O progresso deve serin
formado em background dentro do mtodo doInBackg round( ). Para
reportar o progresso, utilizado o mtodo pub1shProgress(int).
onPostEecute() Mtodo executado na UI Thread, em que podemos atualizar
a view com o resultado. Ele chamado utilizando um Handler
internamente.
Note que esses mtodos no devem ser invocados manualmente, pois so chama
dos automaticamente pela classe AsyncTask. Portanto, para iniciar o processamento,
necessrio apenas chamar o mtodo AsyncTask.execute(params. . .), informando os
parmetros se necessrio, como por exemplo: `
new Down'LoadF1esTask().eecute(ur11, ur12, ur13);
Talvez a parte mais difcil de entender na classe AsyncTask sejam os seus trs tipos
genricos, que tm a seguinte denio: AsyncTask<Params, Progress, Resu1t>.
340 Google Android - 4 edio
Parmetro Descriao g d_ _ __ ____ _ _`_ W
Pa rams O primeiro tipo genrico chamado de Parans, que so os argumentos
que podemos passar ao mtodo eecute(params. . .) para executar 0
AsyncTask. No exemplo da classe DownloadFilesTask, o parmetro foi
denido como URL, e portanto a tarefa pode ser executada passando
URLs como parmetro ao mtodo eecute( ) , ex: new DownloadFilesTask(),
eecute(url1, url2, url3).
Progress O segundo tipo genrico chamado de Progress, e pode ser utilizado
para receber um valor inteiro, que representa o progresso da exe
cuo, e em conjunto com uma barra de progresso, para noticar
o usurio. No exemplo que criamos, esse parmetro foi denido
como Integer, e o mtodo onProgressUpdate(Integer. .. progress) cou
com essa assinatura.
Result O terceiro tipo genrico chamado de Result, e o mesmo objeto
que retorna do mtodo doInBackground() passado como parmetro
para o mtodo onPostEecute(). O mtodo onPostEecute() executa na
UI Thread e pode atualizar a interface. No exemplo que criamos,
o retorno do mtodo doInBackground() era do tipo Long, e o mtodo
onPostEecute(Long result) recebe um Long como argumento.
Eu sei, parece complicado, mas logo voc se acostuma com a ideia. Mas isso nada
mais do que o conceito de Genetics da linguagem Java. Qualquer dvida, con
tinue lendo e depois volte aqui para revisar os conceitos, no se preocupe se no
entender exatamente o que significa cada parmetro agora.
Outra informao importante que, ao criar a AsyncTask, voc pode denir os ti
pos genricos que quiser e deixar alguns como Void. O prximo exemplo mostra
como deixar todos os argumentos como Void e somente o argumento Result como
Boolean. Veja como ca a sintaxe:
}
Captulo 10 I Threads, Handler e AsyncTask 341
s vezes, um cdigo e um exemplo falam mais que mil palavras. Vamos criar nova
mente o exemplo do download da imagem, mas desta vez utilizando a classe AsyncTask
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static nal String URL = "http://livroandroid.com.br/imgs/livro_android.png";
private ProgressBar progress;
private Imageview imgview;
private Bitmap bitmap;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity;download_imagem);
imgView = (ImageView) ndViewById(R.id.img);
progress = (ProgressBar) ndViewById(R.id.progress);
// Faz o download
downloadImagem();
}
@0verride
protected Bitmap doInBackground(Void... params) {
// Faz o download em uma thread e retorna o bitmap.
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
Log.e("livroandroid",e.getHessage(), e);
}
return bitmap;
}
342 Google Android - 4' edio
protected void onPostExecute(Bitnap bltnap) {
if(bitmap != null) {
// Atualiza a imagen na UI Thread
ingvew.setImageBtmap(btnap);
// Esconde o progress
progress.setVisiblity(View.INVISIBLE);
}
Nota: uma das vantagens de utilizar a classe AsyncTask que possvel parar a
execuo da tarefa com o mtodo cancell(boolean). Quando uma tarefa cancelada,
o mtodo onCancelled() chamado. Para descobrir se uma tarefa j foi cancelada,
basta chamar o mtodo isCancelled(). comum nos mtodos onPause() da activity
e fragment chamar o mtodo cancell(boolean) da AsyncTask; assim, quando a activity
vai entrar em estado de pausa, essas tarefas so interrompidas para economizar
recursos, uma vez que a tela ser fechada. Tudo depende do caso e se voc deseja
ou no parar a execuo da tarefa.
compile 'com.squareup.picasso:picasso:2.5.2'
}
Depois de declarar adependncia, o download pode ser feito com apenas uma
nica linha de cdigo.
Picasso.with(this).1oad(URL).into(imgView);
Tambm possvel especicar uma imagem que deve ser exibida antes de o
download terminar (placeholder) e uma imagem de erro.
Picasso.with(this).1oad(URL).placeholder(R.drawab1e.android).error(R.drawab1e.android)
.into(imgView);
Caso voc queira animar um ProgressBar como fizemos nos exemplos anteriores,
basta mostrar o progresso antes do download, e escond-lo quando o download
terminar. A biblioteca Picasso oferece a interface de Callback para avisar o aplicativo
sobre o resultado do download, seja sucesso ou falha.
progress.setVisibi1ity(View.VISIBLE);
Picasso.with(this).1oad(URL)
.p1aceho1der(R.drawable.android)
.error(R.drawab1e.android)
.into(imgView, new Ca11back() {
@Override
public void onSuccess() { // OK
progress.setVisibility(View.GONE);
}
@0verride
public void onError() { // ERRO
progress.setVisibi1ity(View.GONE);
}
});
Dica: mostrei como animar um ProgressBar para seu aprendizado, e isso muito
utilizado em vrias situaes. Porm, no caso de download de imagens, parece
que est havendo uma tendncia de utilizar apenas uma imagem temporria
durante o download, chamada de placeholder. Um exemplo a navegao no
aplicativo do Google Play que mostra uma imagem vazia enquanto o download
344 Google Android - 4 edio
do cone do aplicativo no termina. Nesse contexto, a biblioteca Picasso
extremamente til, pois faz o download em uma nica linha de codigo.
http://squaregithub.io/picasso
.\ !.
CAPTULO 11
Material Design
\-1
.xl p
Com a chegada do Android 5.0 (Lollipop), foi criado o Material Design, um guia
completo sobre como criar aplicaes com um timo design e que leva em consi
derao que atualmente o Android est difundido em diversos dispositivos como
smartphones, tablets, wearables, culos, TVs e carros.
Com esses avanos da plataforma, foi necessrio criar um guia de Design, e princi
palmente uma interface que funcione de forma consistente, independentemente da
plataforma e do tipo do dispositivo, seja um pequeno relgio ou uma grande TV
11.1 Introduo
O Material Design um guia completo sobre como implementar o visual, anima
es e interao entre os componentes de um layout, considerando que o Android
se tornou uma plataforma comum para vrios dispositivos, como smartphones e
tablets (Android), wearables (Android Wear), culos (Google Glass),TVs (Android
TV) e carros (Android Auto). `
Implementar o visual de um aplicativo de forma consistente, simples e intuitiva para
cada tipo de dispositivo um desao, e o Material Design o resultado do esforo
do Google de padronizar um guia completo de design para nos auxiliar nessa tarefa.
No Android 5.0 (Lollipop), foram criadas diversas APIs para auxiliar o desenvol
ver a criar interfaces ricas, uidas e com animaes iguais quelas encontradas
nos aplicativos nativos do Google. O melhor de tudo que podemos utilizar
uma biblioteca de compatibilidade para trazer os benefcios do Material Design,
inclusive para dispositivos com verses antigas do Android.
A gura 11.1 da documentao ocial do Android e mostra a ideia do Material Design:
um aplicativo que tem um design consistente em diversos tipos de dispositivos.
345
346 Google Android - 4' edio
...z .. .. _...--zz
.__,,.,,.`,,,,,
,.. ..-zm
..-..~. zz...
W.
. a.....
.z..........`$--
...uv I
android:/Theme.Material.Light
androd:/Theme.Materia1.Lght.DarkActionBar
Theme.AppCompat.Lght
Theme.AppCompat.Lght.DarkActonBar
Lembre-se de que, para utilizar o tema AppCompat, voc deve declarar no arquivo
app/build.gradle a dependncia para a biblioteca supportzappcompat-v7 e todas as
activities devem herdar de AppCompatActvty.
Captulo 11 I Material Design 347
app/buiId.gradIe
dependencias {
compila 'com.androd.support:appcompat-v7:22.1.9'
}
_1, l
Figura 11.2 - Cores para customizar 0 tema Material.
343 Google Android - 4 edio
Para brincarmos, crie o projeto Helloaterial, ou abra o projeto de exemplo deste captu
lo. Ao criar o projeto com oAndroid Studio, deike ele compatvel com o Android 23 ou
superior e ative a biblioteca de compatibilidade appconpat-v7 no arquivo app/build.gradle_
Lembre-se tambm de que a MainActivity deve ser lha de AppCompatActivity.
<iten name:"colorPrinary"@color/prinary
<iten name:"colorPrinaryDark">@color/prinary_dark
<iten name="colorAccent">@color/accent
Estou customizando as cores no arquivo do tema, e por isso criei estas cores no
arquivo /res/values/colors.xml.
/res/values/coIors.xmI
<resources
<color name="primary">#03A9F4
<color name="primary_dark">#01579B
<color nane="accent">#F44336
Simples assim. Se voc executar o projeto com essas conguraes, a cor da action
bar ser azul, a cor da status bar ser um azul escuro, e a cor de acentuao uti
lizada para dar destaque vermelho.
Nota: a cor primria (primary) deve ser a cor principal do aplicativo. A cor primria
escura (primary escura) uma variao escura da cor primria e utilizada na
status bar. A cor de acentuao (accent color) de extrema importncia para
destacar views importantes do aplicativo e chamar a ateno do usurio.
Captulo 11 I Material Design 349
Para escolher essas cores, o Google recomenda seguir a paleta de cores, que pode
ser encontrada nesta pgina da documentao ocial:
http://www. google. com/design/spec/style/color html
Voc vai encontrar diversas cores nessa ina e todas elas tero vrios nveis
9
Ci /res/Iayout/activity_exempIo_eIevation.xml
<LnearLayout . . .>
<Button android:d="@+d/button" androd:tet="@strng/he1Io_wor1d"
androd:1ayout_wdth="wrap_content" android:1ayout_height="wrap_content"
androtd:eIevatton="2dp" />
<SeekBar androd:id="@+d/seekBar"
sty1e="?android:attr/progressBarSty1eHorzonta1"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
androd:nax="100" androtd:paddng="2Gdp" androd:progress="0" />
</LnearLayout>
A SeekBar uma view que se parece com um controle para aumentar ou diminuir
o volume do som; neste caso, vai permitir navegar entre O a 100, pois foi definida a
propriedade androd:ma="100". No codigo da activity sempre que o valor da Seekbar
for alterado, vamos congurar esse mesmo valor na elevao do botao.
(5131 ExemploE|evationActivity.java
. aowm; ueio
. __ u zz z z~ zz z z ,Q ` ,~. uw, .
l
O efeito de ripple alcanado pela classe Rpplerawable, que pode ser criada por
programao, ou at mesmo via XML criando uma tag <rpple>. Neste prximo
exemplo, vamos testar em um boto vrios efeitos diferentes. Para isso abra o
arquivo /res/layout/activity_exemplo_ripple.xmI no editor visual (Figura 11.6).
* bel ii] l
.zirtriry_eempIo_r|ppIe xml \ 1
O ..
, Nexus S' v AprTheme mv -'
1 .,..
i
'W*V'- z'.-
2..=;-ir
rf cr
'-M, mg;
1
l
'NG
l
Cada boto est congurado com um efeito diferente, mas para entender a expli
caao, por favor, execute o projeto no emulador para visualizar o efeito. No tem
como demonstrar as animaes no livro.
<Button android:text="@strng/ok2" . . .
android:background="?attr/se1ectab1eItemBackground" />
()KI2
()K2
()K2
Nota: a animao de ripple parecida com o efeito de quando voc toca alguma
superfcie com gua, formando aquelas ondas circulares.
<Button androd:tet="@strng/ok3" . . .
androd:background="?attr/se1ectab1eItemBackgroundBorder1ess" />
OK3
OK4
/res/drawable/rppIe_rect.xmI
<rpp1e xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:co1or="@co1or/accent">
<tem androd:d="@androd:id/mask">
<shape androd:shape="rectang1e">
<so1d androd:co1or="@co1or/primary" />
</rpp1e>
Esse arquivo customizado pode ser denido como o fundo de qualquer view
conforme demonstrado a seguir.
<Button androd:text="@strng/ok4" . . .
android:background="@drawable/rpple_rect" />
/res/Iayout/activity_exempIo_oating_button.xmI
<FrameLayout n1ns:androd="http://schens.androd.con/apk/res/androd"
android:1ayout_wdth="natch_parent" androd:1ayout_heght="natch_parent"
androd:paddng="16dp">
<TextVew android:tet="@strng/he11o_wor1d"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content" />
<ImageButton`androd:d="@+d/bt0k"
androd:1ayout_wdth="56dp"androd:1ayout_heght="56dp"
androd:1ayout_gravity="center"
android:src="@androdzdrawable/c_nput_add"
android:tint="#fffB00"
android:background="@drawable/fab_ova1"
androd:e1evation="8dp" />
</FraneLayout
Esse boto foi congurado com uma imagem nativa do Android, sendo que a
notao @androd:drawab1e/ nonejnagem utilizada para acessar um recurso nativo do
Android. Em nosso exemplo, estamos utilizando a imagem c_nput_add. Se voc
abrir essa figura com a tecla CtrI+cIique no editor, ver que ela um sinal de mais
(+) transparente, conforme a figura 11.9.
1 -. : c \' _
' 'z^'. '. U-'zz '~ r, *.
..-.
-,..;, ' .-uy...
ni 1 rf x'f':' .z \ viu \` w ~:
..".`=..'"," ' -`."-X
-1' '1'z``.'--'- _ v'` _
..,.zzz.z,.-,r;.
O segredo dessa imagem que a rea do sinal de mais (+) pode ser preenchida
com qualquer cor, e por isso definido o atributo androd:tnt="#fff000" que est
pintando a cruz de amarelo. j o fundo do boto feito com um efeito de ripple,
sendo assim basta criar um XML customizado conforme demonstrado a seguir:
356 Google IIO - 4 edio
/res/drawable/fab_ovaI.xmI
<?m1 verson="1.0" encoding="utf-8"?>
<rpp1e m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:co1or="?androd:coIorPrimary">
</tem> `
<tem>
<shape androd:shape="ova1">
z<so1d androd:co1or="?android:co1orAccent" />
</shape
E isso tudo! Neste exemplo, a gura c_nput_add mostra o sinal de mais (+), qug
padro do Android, e o fundo do ripple criou um shape=ova1, por isso a imagem ficou
como um crculo. O fundo do ripple foi denido como vermelho, que a cor de
acentuao congurada no projeto. A gura 11.10 mostra o resultado, e quando voc
executar no emulador ver que o boto tem o Touch Feedback com o efeito do ripple.
4 u 2 33
-' . ', -1 z2}`-1" -=\' f' ' QC. .. ""!K"i " 1'-*"'*'
-,ty-~
gw* .Lg *f
-,r _.:f'. , .. law;-'52
f z.1..1~^. ~*,_;.z.V`
1.- ,f,._-_~..._Wz.
~ _=;1 ..:%zf4_ zm _-vil.
.t da w~~
Q
lif:(_.'~z`V,..V.\,.
, z'_ V ~ fist.. .. 'Ja
,z -' .'l.;"V. _2`.. Ez
..' -1^*-'
; ._
Q A :'.:'u; z z' ,4;^-gi. _$'z., . - N-.i.{v.,_-Qz, g {--rw ;_'.'_, _ * 2
,_<.,,;'-';._
"' *
'ijg'
-fr?;&:#z
i;_z=\_,
.
Nota: a imagem do sinal de mais (+) transparente e conhecida como Tint Drawable
Resources. No Android 5.0 (API Level 21) ou superior possvel criar as figuras
como nine-patches e demr uma msca ra transparente (alpha masks). Essa rea
transparente pode ser pintada (tint) com a cor desejada
ltsse exemplo que fizemos sobre o boto FAB interessante porque voc apren
deu conccitos importantes como Tin! Drawablc Resource; Q ngvameme utilizamos
Captulo 11 I Material Design 357
o efeito de ripple. Porm, logo depois do Google I/O 2015, o Google anunciou a
biblioteca Android Design Support Library que contm classes e componentes para
auxiliar na construo de aplicativos com Material Design. Dentre esses com
ponentes, foi criada a classe F1oatngActonButton, que uma subclasse de Inageview
e torna simples a tarefa de criar um Floating Action Button. Para utilizar a biblioteca
Android Design Support Library, declare a dependncia no app/buildgradle.
app/buiId.gradIe
conpe 'con.android.support:desgn:22.2.0'
11.7 CardView
O Material Design recomenda a utilizao de cartes (cards) sempre que for preciso
separar a visualizao de determinados elementos. O visual parecido com o que
temos na lista do Google Now em que diversos cartes so mostrados em uma
lista. Cartes tambm podem ajudar a organizar o contedo de aplicativos que
mostram informaes em listas ou grids, como feito no Google Play oferecendo
uma melhor visualizao ao usurio.
A gura 11.11 mostra o visual obtido com os cartes no aplicativo do Google Play
A principal ideia por trs da utilizao dos cartes proporcionar uma interface
consistente em todas as plataformas do Android, desde smartphones, tablets e at
relgios. Sendo assim, criar um carto (card) a tarefa mais simples, o importante
voc entender o real significado de utiliz-lo, que seguir as boas prticas de
interface do Material Design e oferecer um design de interface consistente para
vrios tipos de dispositivos.
358 Google Android -4' edio
g 'ij' 1 0 'q5`
:`f.f. PAGINAWCW = f
Apps novos e atualizados W
.{ *i`
JI
I; } ~, u
'._*1@\_ . af
' :`'<\`
/T;_^='i
ta .
L 1.tmam;
M d Anywhere
Aifbb E Send 2
Gostinho Brasileiro
App:+;:i-10<:rna|~: xiriizffts m
L: .. _ , ~'
_ =,i ,
~S''z= *fo
Muda voz 2 STAR UUSWYGSW
com efeitos WARS' 7 g
Agora vamos falar um pouco de cdigo. A classe do Cardvew distribuda por meio
de uma biblioteca de compatibilidade do Google, portanto declare a seguinte
dependncia no projeto:
app/buiId.gradIe
dependences {
/res/Iayout/activity_eempIo_tard_view.xmI
<LinearLayout m1ns:android="http://schemas.android.com/apk/res/androd"
xm1ns:card_view="http://schemas.androd.com/apk/res-auto"
android:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:paddng="16dp" androd:orentaton="vertica1">
Captulo 11 I Material Design 359
<android.support.v7.wdget.ardVew
androd:id="@+d/cardvew"
android:1ayout_wdth="100dp" androd:1ayout_heght="10Gdp"
card_vew:contentPaddng="6dp"
card_vew:cardBackgroundCo1or="?attr/colorPrmary">
<TextView
android:text:"@string/he11o_wor1d"
android:1ayout_wdth="wrap_content" androd:layout_heght="wrap_content"
android:1ayout_gravity="center"
androd:tetCo1or="?attr/co1orAccent" />
/android.support.v1.widget.CardVew
SeekBar android:id="@+id/seekBar1"
sty1e="?androdzattr/progressBarSty1eHorzonta1"
androd:1ayout_wdth="11_parent" android:1ayout_heght="wrap_content"
androd:max="100" androd:paddng="20dp" android:progress="0" />
SeekBar android:id="@+d/seekBar2"
sty1e="?androd:attr/progressBarSty1eHorzontal"
androd:1ayout_wdth="11_parent" android:1ayout_heght="wrap_content"
android:ma="100 androd:paddng="20dp" android:progress="0" />
ExempIoCardViewActivity.java
public class EemploCardVewActvity extends AppCompatActivity mplements SeekBar.
OnSeekBarChangeListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVew(R.layout.actvity_eemp1o_card_vew);
cardvew = (Cardvew) ndViewById(R.id.cardVew);
@Overrde
360 Google Android - 4 edio
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromser) {
if (seekBar == this.seekBar1) {
cardview.setCardE1evation(progress); // Elevao
} else if (seekBar == ths.seekBar2) {
cardvew.setRadius(progress); // Arredondamento
1
A gura 11.12 mostra o resultado. Veja que como o segundo SeekBar est com o valor
cheio, a borda do carto cou arredondada. Sugiro que voc execute o cdigo no
emulador para visualizar o resultado.
Nota: o Cardvew muito utilizado nos aplicativos nativos do Android. Se voc notar,
o Google Now composto por cards e o Google Play tambm mostra a lista de
aplicativos com cards. Esse efeito de carto ajuda a separar uma view da outra, pois
cria essas bordas. No aplicativo dos carros que logo vamos comear a desenvolver
vamos utilizar o Cardvew para separar as informaes de um carro para outro.
11.8 RecycIerView
Desde a primeira verso do Android, o Listvew sempre foi a view padro para
criar listas, mas sempre que era necessrio criar efeitos customizados ele tornava
a vida do desenvolvedor um pouco difcil. Outra questo importante que se
o desenvolvedor no soubesse otimizar a rolagem da lista, por meio do padro
VewHo1der, o aplicativo acabava cando com a rolagem da lista prejudicada, de
uma forma no fluida e com travamentos.
Captulo 11 I Material Design 361
Com o surgimento do Material Design, tambm foi criado o Recyclervew, que
o novo Lstvew do Android, e a partir de agora a view recomendada para criar
listas segundo as boas prticas de interface.
O Recyclervew apresenta algumas funcionalidades interessantes:
Suporte a animaes ao adicionar ou remover elementos da lista.
Controle automtico da reutilizao das views (padro VewHo1der).
Permite alterar o gerenciador de layout para renderizar as views como listas,
grids, ou outra forma customizada.
Para usar um Recyclervew, basta inclu-lo no layout e informar uma subclasse de
Recyclervew. LayoutManager, conforme demonstrado a seguir.
Recyclerview recyclervew = (Recyclervew) vi.ew.ndViewById(R.i.d.recyclerview);
Recyclerview.LayoutManager mLayoutManager = new LnearLayoutManager(getActvty());
recyc1erVew. setLayoutManager(mLayoutManager);
recyclerview. setItemAnnator(new Defau1tItenAninator( ) );
Nota: o padro ViewHo1der sempre foi utilizado para fazer a rolagem de um Lstview.
No cheguei a explic-lo aqui no livro porque vamos utilizar o Recyclerview
daqui para frente, e ele j faz a rolagem de forma eciente. Mas vamos explicar
resumidamente o que o VewHo1der. Quando o Android faz a rolagem em uma
lista com uma grande quantidade de elementos, preciso reutilizar as views
para evitar criar uma nova a cada item. Imagine que exista uma lista com 1.000
linhas. Como o Android mostra no mximo umas 10 views por vez, podemos
criar apenas 10 views e reutiliz-las ao fazer a rolagem, em vez de criar 1.000
objetos do tipo view. Isso otimiza a memria e deixa a rolagem da lista uida.
Com a utilizao do Recyclervew, a implementao desse padro feita de forma
transparente ao desenvolvedor.
362 Google Android - 4 edio
O Recylerview na verdade um componente especializado no reaproveitamento das
views, ou seja, ele recicla as views e implementa automaticamente o padro Viewolder,
para garantir um bom desempenho ao fazer rolagem com uma grande quantidade de
itens Mas o Recylervew no sabe desenhar nada na tela, e para isso ele precisa de alguma
subclasse de LayoutManager, a qual responsvel por desenhar e organizar a disposio
das views A lista a seguir mostra algumas das subclasses de Recyc1erView.LayoutManager:
LinearLayoutManager
GridLayoutHanager
StaggeredGridLayoutManager
recyclervew.setItenAnimator(new Defau1tItemAnimator());
}.
maes bsicas quando um item da lista adicionado, removido ou movido de
posio. Depois de inserir alguma informao na lista que a fonte do contedo
do adapter voc pode chamar o mtodo notifyItemInserted(idx) para informar que
um item foi adicionado ou o mtodo notifyItemRemoved(id) para informar que um
item foi removido da posio indicada.
P' .
Depois dessa explicao, vamos para a parte prtica. Primeiramente, para utilizar
o Recyclerview preciso declarar a seguinte dependncia:
@ app/bu|Id.gradIe
dependencies {
conpile 'con.android.support:recyclerview-v7:21.6.+'
Captulo 11 1 Material Design 363
A seguir podemos visualizar o arquivo de layout com o Recyclerview.
/res/Iayout/attivity_eempIo_recyc|er_view.xmI
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.Recyclerview
android:id="@+id/recyclerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
ExempIoRecyclerViewActivity.java
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Recyclerview;
public class ExemploRecyclerViewActivity extends AppCompatActivity {
private Recyclerview recyclerview;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eemplo_recycler_view);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Recyclerview
recyclerview = (Recyclerview) ndViewById(R.id.recyclerView);
recyclerview.setLayoutManager(new LinearLayoutManager(this));
recyclerview.setItemAnimator(new DefaultItemAnimator());
recyclerview.setHasFixedSize(true);
// Planetas e Adapter
List planetas = Planeta.getPlanetas();
recyclerview.setAdapter(new PlanetaAdapter(this, planetas, onClickPlaneta()))
}
// 0nClick Planeta
private PlanetaAdapter.Planeta0nClickListener onClickPlaneta() {
return new PlanetaAdapter_PlanetaOnClickListener() {
364 ' 4 Ed0
@0verride
public void onClickPlaneta(View view, int idx) {
List planetas = Planeta.getPlanetas();
Planeta p = planetas.get(idx);
Toast.makeTet(getBaseContet(), "Planetaz " + p.n0me, Toast LENGTH_SH0RT) Sh0w();
}
};
}
@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_linear_layout) [
// Troca o modo de visualizao para lista
recyclerview.setLayoutManager(new LinearLayoutManager(this));
return true;
} else if (id == R.id.action_grid_layout) {
// Troca o modo de visualizao para grid
recyclerview.setLayoutManager(new GridLayoutManager(this, 2));
return true;
}
return super.on0ptionsItemSelected(item);
}
P|anetaAdapter.java
import android.support.v7.widget.RecyclerView;
// Herda de RecyclerView.Adapter e declara o tipo genrico <PlanetaAdapter.
PlanetasViewHolder
Captulo 11 n Material Design 365
public class PlanetaAdapter extends Recyclerview.AdapterPlanetaAdapter.PlanetasViewHolder {
protected static nal String TAG = "livroandroid";
private nal List planetas;
private nal Context context;
private nal Planeta0nClickListener onClickListener;
public interface Planeta0nClickListener {
public void onClickPlaneta(View view, int idx);
}
public PlanetaAdapter(Context context, List planetas, Planeta0nClickListener
onClickListener) {
this.context = context;
this.planetas = planetas; ,
this.onClickListener = onClickListener;
}
@Override
public PlanetasViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Este mtodo cria uma subclasse de RecyclerView.ViewHolder
// Ina`a view do layout
View view = Layoutlnater.from(context).inate(R.layout.adapter_planeta,
viewGroup, false);
// Cria a classe do ViewHolder
PlanetasViewHolder holder = new PlanetasViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(nal PlanetasViewHolder holder, nal int position) {
// Este mtodo recebe o indice do elemento, e atualiza as views que esto
// dentro do ViewHolder
Planeta c = planetas.get(position);
// Atualiza os valores nas views
holder.tNome.setText(c.nome);
holder.img.setImageResource(c.img);
// Click
if (onClickListener != null) {
holder.itemView.set0nClickListener(new View.0nClickListener() {
@Override
public void onClick(View v) {
// Chama o listener para informar que clicou no Planeta
onClickListener.onClickPlaneta(holder.view, position);
1
});
}
}
366 Google Android - 4 edio
@0verride
public int getItemCount() {
return this.planetas != null ? this.planetas.size() : 6;
}
O resultado do exemplo pode ser visualizado na gura 11.13, que mostra a visu
alizao no formato de lista e grid.
I Wl
z rtof ef lJ
Mercrio Mercrio 0 Vnus
li
~rm i
T"3 p Jupiter ` Saturno
` I p p,
A' Mam* i 8 Urano O Netun
E
});
}
};
}
ReveaIEffect.java
@rrgerAp(Buud.vERs1oN_coDEs.LoLL1PoP)
public class RevealEffect {
public static void show(View view, long animDuration) {
// Centro da view
int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
// Dene o arco para a animao
int nalRadius = Math.ma(view.getwidth(), view.getHeight());
// Cria a animao
Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, nalRadius);
// Inicia a animao
view.setVisibility(View.VISIBLE);
anim.setDuration(animDuration);
anim.start();
}
});
// Inicia a animao
anim.setDuration(animDuration);
anim.start();
1
Para testar esse efeito de animao, criei um layout simples com dois botes que
chamam os mtodos show() e hide() dessa classe. A imagem do planeta que est no
centro do layout vai receber o resultado da animao. A gura 11.14 mostra a anima
o de revelao executando no emulador, mas o ideal voc conferir no emulador.
No cdigo-fonte da activity a tarefa necessria obter a view que precisa ser
E11snow
. .1.1l1 |Fsnow~uma
animada e chamar os mtodos para mostrar ou esconder a view
ii snow
' I1-nos 'mos
ExempIoRevea|EffectActivity.java
};
}
};
}
});
Google Android "' 45 Edio
Depois que a paleta de cores extrada da gura, podemos tentar ler as tonali
dades de cores que foram extradas da gura, que so: Vibrant, Vibrant Dark, Vibrantligln,
Muted, Muted Dark e Muted Light. Para fazer isso, basta chamar algum mtodo da classe
Paiette, como o mtodo getvibranttoior(defaul.tCo1or).
Se quiser brincar um pouco com a paleta de cores, execute no emulador o exemplo
que est disponvel no projeto deste captulo. Nele, pinto vrios Textviews que esto
na tela com as cores que so extradas de uma gura do planeta Terra.
Idem ao mtodo anterior, mas permite passar uma lista de views a serem
compartilhadas durante a animao de transio.
Nota: o aplicativo do Google Play usa e abusa deste novo recurso. Na pgina
inicial que lista os aplicativos da loja, ao clicar em algum aplicativo, a nova
activity e aberta e o icone do aplicativo move-se at a nova posio.
Para utilizar esse novo recurso de animao, a transio de janelas precisa estar
habilitada, o que pode ser feito congurando 0 AndroidManifest.xml ou dinami
camente no cdigo de cada activity
Basicamente podemos definir a animao de entrada (enter) ao abrir a tela, a
animao de sada (exit) ao sair da tela e ainda compartilhar elementos (shared
elements) entre as telas.
Captulo 11 I Material Design 373
Para habilitar o modo de transio de telas, temos duas formas. A primeira
congurar o atributo windowContentTransitions para true na congurao do tema,
como demonstrado a seguir.
<styie nane="BaseAppThene" parent="android:Theme.Materia1">
<iten nane="android:windowContentTransitions">true
||n
<iten nane="android:windowContentTransitions">true
<iten name="android:windowEnterTransition">@transition/ep1ode
<item nane="android:windowExitTransition">@transition/exp1ode
Outra opo habilitar o modo de transio entre activities pelo cdigo. Basta
chamar o mtodo getllindow( ) . requestFeature(window. FEATURE_CONTENT_TRANSITIONS). Eu
costumo utilizar essa opo. S tenha ateno, pois para a transio funcionar esta
chamada precisa ser feita na primeira linha de cdigo do mtodo onCreate(bundie) da
activity e deve ser includa em ambas as activities, origem e destino da animao.
Mas chega de teoria, vamos praticar um pouco de cdigo. Copie o projeto
Hellollctivityiransition que zemos no captulo 9, sobre animaes, pois ele ser a base
para continuarmos. No captulo 9 j aprendemos a fazer transies de transparn
cia, chamadas de fade_in e fade_out, assim como transies de movimento, chamadas
de sIide_in e sIide_out.
] MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClickPlaneta(View view) { \
Intent intent = new Intent(getBaseContet(), PlanetaActivity.class);
startActivity(intent);
}
PIanetaActivity.java
public class PlanetaActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
// Habilita a transio de telas (deve ser a primeira linha de cdigo)
getHindow().requestFeature(Hindow.FEATURE_CONTENT_TRANSITIONS);
super . onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
l
Captulo 11 n Material Design 375
public void onClickPlaneta(View view) {
Intent intent = new Intent(getBaseContet(), PlanetaActivity.class);
// Efeito padro de cross-fading
Activity0ptionsCompat opts = Activity0ptionsCompat.nakeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, opts.toBundle());
}
1
\
PIanetaActivity.java
public class PlanetaActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
getNindow().requestFeature(Nindow.FEATURE_CONTENT_TRANSITIONS);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
getSupportActionBar().setDisplayHoneAsUpEnabled(true);
}
/res/layout/activity_man.xmI
<FrameLayout m1ns:android="http://schemas.androd.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent">
<ImageVew androd:d="@+d/img"
androd:transtonName="@string/transtion_key"
android:1ayout_gravty="center"
androd:src="@drawable/p1aneta_03_terra"
androd:1ayout_width="100dp" androd:1ayout_heght="100dp"
androd:onC1ck="onC1ckP1aneta" /
A chave @strng/transton_key pode ter qualquer texto e serve apenas para identi
car esta view:
P /res/values/strings.mI
/res/layout/activity_pIaneta.xmI
<FraneLayout . . .>
<ImageView android:id:"@+id/img"
android:transitionNane="@string/transition_key" . . ./>
MainActivty.java
public class MainActivity extends AppConpatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
// Habilita a transio de telas (deve ser a primeira linha de cdigo)
getHindow().requestFeature(Nindow.FEATURE_CONTENT_TRANSITIONS);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
}
Pronto, isso tudo. Agora execute o cdigo novamente e veja a mgica acontecer.
Caso seja necessrio, fazer a transio de mais uma view ao mesmo tempo tam
bm possvel, pois a assinatura do mtodo makeSceneTransitionAnination(. . .) pode
receber uma lista de android.support.v4.util.Pair<View, String>.
nakeSceneTransitionAnimation(Activity activity, Pair<View, String>... sharedElenents)
Ento digamos que no layout exista um Textview com o nome do planeta e a gura
Nesse caso, poderamos fazer a transio de telas com este cdigo:
378 Google Android - 4 edig
Intent intent = new Intent(getBaseContet(), PIanetaActivity.c1ass);
Pair p1 = Pair.create(ndViewById(R.id.img), getString(R.string.img_transition_key));
Pair p2 = Pair.create(ndViewById(R.id.img), getString(R.string.tit1e_transition_key));
ActivityOptionsCompat opts = Activity0ptionsCompat.makeSceneTransitionAnimation(this, p1, p2)
Activitytompat.startActivity(this, intent, opts.toBundIe());
http://android-developers.blogspot.com.br/2014/10/implementing-material-desigm
-in-your: html
,
\z
Com o lanamento do Android 5.0 (Lollipop) foi criada a Toolbar, uma view
especial que pode ser inserida em qualquer lugar do layout e tem as mesmas
funcionalidades da action bar.
A Toolbar est sendo muito utilizada para criar aplicativos seguindo as boas
prticas de interface do Material Design.
380
Captulo 12 I Toolbar 331
Outro exemplo de utilizao da Toolbar pode ser visto na gura 12.2, que mostra
um contato selecionado no aplicativo e a Toolbar mostra as aes que podemos
fazer com aquele contato. Como podemos ver, a vantagem de utilizar a Toolbar
que ela uma view parecida com a action bar, portanto os usurios vo reconhe
cer esse padro e vo se sentir familiarizados com ele. AToolbar pode mostrar as
principais aes com os actions buttons e inclusive ter o menu flutuante action
overflow com as opes menos utilizadas.
Como Toolbar uma view comum os aplicativos criarem animaes para mover
a Toolbar ou redimension-la. Um exemplo disso o aplicativo da agenda e ligao
do Android 5.0. A gura 123 mostra um contato da agenda e ao seleciona-lo (clicar
nos trs pontinhos) uma view com a Toolbar aparece na tela com uma animao
de baixo para cima ( direita da figura).
li
Q, Type a name or phone number
T | _l 5
Ricardo Lecheta ea
fi Mobile, O mins ago
Nesse momento, se voc tocar na view da Toolbar e arrasta-la para cima, todo
o contedo aparece por cima da tela original, at a Toolbar se fixar no topo
382 Google Android - 4' edigo
(Figura 114). Caso o contato tenha muitas informaes cadastradas e voce conti
nuar fazendo a rolagem para cima, aToolbar redimensionada com uma animao
e fica pequena igual action bar ( direita da figura).
Egz exemplo mostrou o poder e a flexibilidade da Toolbar se comparada action
bar. e acredito que tenham cado claras as diferenas. O recomendado voc
brincar como esse aplicativo no emulador ou no seu smartphone para visualizar
as animaes de movimento que so muito fluidas.
5 f E T 777)?77~77?? D
zz wa 9999
-\` \
Agora que sabemos o basico sobre a Toolbar, vamos estudar um pouco de codigo.
A maneira mais simples de utilizar a Toolbar desligar a action bar nativa, inserir
a Toolbar no layout e configura-la para tomar o lugar da action bar. lsso e simples
e consiste em trs passos:
Captulo 12 I Toolbar 333
1. Para desligar a action bar, congure o aplicativo ou a activity desejada para
utilizar o tema Theme.AppCompat.NoActionBar ou Theme.AppCompat.Light.NoActionBar.
app/buiId.gradIe
dependencies {
/res/values/styIes.xmI
AndroidManifest.xm|
<manifest . . . />
<app1ication android:theme="@sty1e/AppThene" >
384 Google Android - 4' edio
<acti.vity android:nane= .Ha'tnActivity" androtd:thene="G$tY1/ PPTNHQ-N0Bir'
/activty
</nanifest
Essa con gurao vai remover a action bar da activity; portanto precisamos inserir
a Toolbar no layout. Para isso. eu gosto de criar um arquivo de layout separado
conforme demonstrado a seguir.
/res/layout/intIude_tooIbar.xm| c
android.support.v7.widget.Toolbar n1ns:android="http://schemas.android.com/apk/res/android"
androd:id='@+id/toolbar"
android:1ayout_heght=wrap_content" androd:layout_wdth="match_parent"
androd:nnHeight="?attr/actonBarSize" android:background="?attr/co1orPrinary" /
Nesse arquivo a altura da Toolbar est configurada como a altura da action bar. Isso
feito acessando o recurso nativo ?attr I actionBarSze. A cor de fundo da Toolbar foi
denida pelo recurso ?attr/co1orPrinary, ou seja, a cor primria do tema Material.
Nota: voc deve ter reparado que a notao ?attr utilizada para acessar um
recurso nativo do Android. lsso interessante. pois podemos obter os valores
padres da plataforma.
a /res/layout/attivity_main.xml
<LinearLayout xmlns:android="http://schemas.android.con/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent">
inc1ude layout:"@1ayout/nc1ude_too1bar" /
<TetView
"<l"Cl1lY0Ut_Wdth="wrap_content" android:1ayout_height="wrap_content"
android:tet="@string/he11o_wor1d" /
</LinearLayout
@Override l
public class MainActivity extends AppCompatActivity {
@Override
public boolean onCreate0ptionsMenu(Menu menu) {
getMenuInater().inate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean on0ptionsItemSelected(Menultem item) {
// Trate os eventos da action bar normalmente aqui.
}
l1i Ili|l
Pronto, isso tudo. A gura 12.5 mostra o resultado. Parece que uma action bar,
mas na verdade uma Toolbar. Inclusive o boto do smile funciona normalmente.
Hello world! p
l
Nota: utilizar a Toolbar como a action bar nos permite continuar utilizando os
mesmos mtodos com que j estamos acostumados da classe ActionBar. Com a
vantagem de que podemos inserir a Toolbar em qualquer lugar do layout e ainda
brincar de anim-la, pois ela uma view como outra qualquer.
MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
No layout da activity vamos adicionar a Toolbar. Para brincar, ela foi inserida na
parte inferior da tela.
/res/layout/activity_main.xml
<FraneLayout . _ .>
<TextView . . ./>
<include layout:"@1ayout/inc1ude_too1bar_1ight"
android:1ayout_width="match_parent" android:1ayout_height="wrap_content"
android:layout_gravity="botton" />
O resultado pode ser visto na gura 12.6. Veja que a activity mostrou a action bar
normalmente l em cima. Na parte inferior podemos ver a Toolbar, que inflou os
itens de menu e tambm ativou o up navigation.
Neste captulo, estudamos o bsico sobre a classe Toolbar e para continuar seus
estudos selecionei alguns links da documentao ocial.
Toolbar - Documentao da API
hrtp://android-developers.blogspot.com.br/2014/10/implementing-material-desigm
-in-younhtml
cAPruLo 13
Navigation Drawer
\
\
,lp j
i_ lr`.`T_} LA
389
390 Google Android - 4= edio
No wizard selecione o Android 23 como a verso mnima (Figura ]3.2). Clique
em Next e na prxima pgina do vvizard escreva Carros no campo Title.
.zf._=.r L5e:~.s__,
app/buiId.gradIe
dependencies {
conpile 1eTree(dir: '1ibs', include: ['*.jar'])
// Ativa a dependncia da biblioteca de compatibilidade v7
conpile 'con.android.supportzappconpat-v7:22.1.1'
}
O projeto tambm foi congurado para utilizar o tema AppConpat. Se por acaso o
wizard no zer isso automaticamente, faa estas alteraes:
/res/values/sty|es.m|
<I"ESOUI'CS>
/res/values/coIors.xmI
<|'SOUICES>
<color name="primary">#03A9F4/color
<color name="primary_dark">#01579B
<color name="accent"#F44336
<color name="control_highlight"#B3E5FC
<color name="transparent">#0000
<color name="white">#fff
<color name="black">#000
<color name="blue">#00f
<color name="red">#f00
<color name="green">#@f0
<color name="gray">#eee
No arquivo de cores j aproveitei e criei algumas cores comuns que podemos utili
zar no aplicativo. Para prosseguir, congure o tema com as cores do tema Material
que so os atributos colorPrimary, colorPrimaryDark, colorAccent e colorControlHighlight
/res/values/styIes.xmI
<item name="colorPrimary">@color/primary
<item name:"colorPrimaryDark">@ClF/DFWBFy_dFk</W>
392 Google Android - 4 edio
<item nane="co1orAccent"@color/accent</iten
iten nane="colorControlighlght">@co1or/contro1_highIight/iten
] /res/values/strings.ml
<'SOU'CeS>
<string nane="app_nane">Carros/string
<string name:"he11o_wor1d">Hel1o wor1d!
<strng name="action_settings">Settings
A gura 133 mostra o projeto executando no emulador. Este livro no foi impresso
com cores, portanto certique-se de que a action bar cou com as cores que voc
deniu no tema.
_ , ll u 10:43
, Yytjg jjf- ' V:
ts .fra-a ~ '., ';*i,,fz
z~;':,'i'f az
"'.-aaa. - .
' *~ 'j'l"`!
1 ;-..f
f ~.._,:~~ zfxmf, ~
.- :.`.,;\c~>". sui' _ l
BaseActivity.java
package br.con.livroandroid.carros.activity;
import android.support.v7.app.AppCompatActivity;
public class BaseActivity extends AppCompatActivity {
}
MainActivity.java
package br.cormlivroandroid.carros.activity;
public class MainActivity extends BaseActivity {
}
BaseFragment.java
package br.com.livroandroid.carros.fragnents;
import android.support.v4.app.Fragment;
public class BaseFragment extends Fragment {
}
1j java
3PP
n.;zaai;r,A____i___W__g_#_~_w__=&_j
l ? manfests
l
j br.com.lb.froandroid.carros
j*C.CzzMainActivit~y
Basezzctivity L.
'- fragments p
j ': BaseFragment
Dica: caso o editor mostre uma linha vermelha porque o Android Studio
identicou um problema. Nesses casos, recomendado abrir o assistente com o
atalho Ctrl+Enter pa ra visualizar as opes. Um exemplo disso quando digita mos
no cdigo um pacote que no existe (ex.: br.con.livroandroid.carros.fragments) c o
__. --' -~ ;' '
assistente pode ajudar a mover a classe pa ra o pacote correto.
o (arrosAppIication.java
package br.com.livroandroid.carros;
import android.app.Application;
import android.util.Log;
public class CarrosApplication extends Application {
private static nal String TAG = "CarrosApplication";
Captulo 13 I Navigation Drawer 395
private static CarrosApplication instance = null;
public static CarrosApplication getInstance() {
return instance; // Singleton
}
@Override
public void onCreate() {
Log.d(TAG, "CarrosApplication.onCreate()");
// Salva a instncia para termos acesso como Singleton
instance = this;
1
@0verride
public void onTerminate() {
super.onTerninate();
Log.d(TAG, "CarrosApplication.onTerninate()");
}
Mais para frente, durante o desenvolvimento do projeto dos carros, vamos utilizar
essa classe.
396 Google Android - 4 edio
13.5 Biblioteca android-utils
No projeto dos carros vamos utilizar a biblioteca android-utils que contem algumas
classes utilitrias criadas para facilitar os exemplos e tambm o seu aprendizado.
O cdigo-fonte da biblioteca android-utils est no GitHub para sua consulta:
https://github.com/livr0android/AndroidUtils/
O objetivo dessa biblioteca facilitar seu aprendizado e principalmente deixar
o desenvolvimento do aplicativo dos carros mais fcil. Para utilizar a biblioteca
android-utils, basta adicionar a dependncia no arquivo app/build.gradIe.
app/buiId.gradIe
apply plugin: 'com.android.application'
dependencies {
compile leTree(dir: 'libs', include: ['*.jar'])
compile 'con.android.support:appcompat-v7:22.1.0'
conpile 'br.con.livroandroid:android-utils:1.9.6'
}
BaseActivity.java
package br.com.livroandroid.carros.activity;
public class BaseActivity extends livroandroid.lib.activity.BaseActivity {
}
Repare que as classes tm o mesmo nome, mas esto em pacotes diferentes. Se voc
tiver duvidas sobre os pacotes, procure alguma leitura complementar. Da mesma
forma, lff 21 CQSSC BSFFH9ment que criamos para ser lha de livroandroid.lib.
fragment.BaseFragment da biblioteca android-utils.
J BaseFragment.java
package br.com.livroandroid.carros.fragments;
public class BaseFragment extends livroandroid.lib.fragnent.BaseFragnent {
}
Captulo 13 I Navigation Drawer 397
Feito isso, salve os arquivos e compile 0 cdigo com a opo Build > Rebuild Project. Se
o projeto compilar, tudo est congurado corretamente, e as classes da biblioteca
androd-utls foram encontradas.
1 . 1
repositrio mundial de projetos utilizado pelo Gradle para buscar as dependncias.
_l .**'
Para validar que a biblioteca existe no repositrio do Maven Central, acesse o
endereo http://search.ma1/en. org e faa a busca por br.con.1vroandrod (Figura 135).
Voc tambm pode adicionar repositrios locais na sua mquina, ou at instalar
is
1 l l
1 . E
um servidor de repositrios privado na rede de sua empresa.
...aaa a--- ~~ - i ,z l pl
,Search' . DoResults
{, '1. GDIYQFSY
l SEARCH 1 AWANCEDSEARCH 1 BROWSE i C.UlCl$
I d ' ll .
br com lrvroandroid l SEARCH ll'
l Nr=vrAbutCenlral Ad d5earch | AFI Guide I 529
Gmupm Amfz Latest Version UPUHEG W"
br corn livroandroid andro1d~ulils LM _f13'll9l Odxmnlzms Em El @ .
:Too few results? Try: fc:br.com.l|vroandro|d l
l
Y4 :~'~_j:;*''-*';f"*
_-,_,_.,._.-.--.~--z-;___ ~~--W-e~~~-*~~f~~'f W- `7'";`"`"
___,:.;:.f:-;;z_:z,.;:::..---.:--: --:::.~ "fa':;:i:;.
--;.:---~~z~--~~ ~~-~z~ V
/res/values/styIes.mI
</resources
/res/layout/include_tooIbar.xmI
<android.support.v7.widget.Too1bar xmins:android="http://schemas.android.com/apk/res/android"
xmins : app="http : //schemas . android . com/apk/res - auto"
android:id="@+id/toolbar"
Captulo 13 n Navigation Drawer 399
android:layout_width="match_parent" androidzlayout height="wrap Content"
android:background="?attr/colorPrimary" android:minHeight="zattr/actongargze"
app:theme:"@style/Thene0verlay.AppConpat.Dark.ActionBar"
app:popupTheme="@style/Theme0verlay.AppCompat.Light" />
/res/layout/activity_main.xml
<LinearLayout xnlns:android="http://schemas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/include_toolbar" /
<TetView android:tet="@string/hello_world"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
BaseActivity.java
package br.com.livroandroid.carros.activity;
import android.support.v7.widget.Toolbar;
public class BaseActivity extends livroandroid.lib.activity.BaseActivity {
protected void setUpToolbar() {
Toolbar toolbar = (Toolbar) ndViewById(R.id.toolbar);
if (toolbar != null) {
setSupportActionBar(toolbar);
}
Em todas as activities
(1 ' no o prOJf0,
odemos nos esquecer P'
de ativar a Toolbar
portanto adicione a seguinte linha de cdigo na classe MainActivity:
MainActivity.java
package br.com.livroandroid.carros.activity;
public class MainActivity extends BaseAct1vity {
@0verride
400 Google Android - 4 edio
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activty_man);
setUpToo1bar();
1
Pronto. Feito isso, execute o projeto, e tudo deve continuar funcionando. A dife
rena que estamos usando a Toolbar no lugar da action bar. A vantagem que a
Toolbar uma view normal, portanto podemos criar animaes de movimento ou
transparncia quando for necessrio. Para ter ideia dessas aniinaes, brinque um
pouco com o aplicativo do Google Play e veja os efeitos que ele faz com a Toolbar.
Antigamente, o Navigation Drawer abria por baixo da action bar, algo que at
que era simples de fazer. No Material Design, como temos de abrir o menu lateral
por cima de tudo, vamos precisar fazer algumas mgicas no cdigo. Mas que
tranquilo que vou ajudar voc a fazer tudo passo a passo.
Ento, mos obra!
Altere o arquivo de layout da activity principal, conforme demonstrado a seguir:
/res/layout/activity_main.xm|
<androd.support.v4.wdget.DrawerLayout xmlnszandrod="http://schenas_androd.com/apk/res/androd"
android:id="@+d/drawer_1ayout"
androd:1ayout width="match_parent" androd:1ayout_heght="natch_parent" >
<LinearLayout androd:d="@+d/content"
androd1ay0Ut wdth:"mtCh_D8Ft" androd:1ayout_heght="natch_parent"
androd:orentaton="vertica1">
<nclude 1ayout="@1)'U'C/lude-t1bar />
<FraneLayout _ H
androd:d="@+id/nav_drawer_c0nf19F
androd.1ay0ut wdth="match_parent" androd:layout_heght="match_parent"/>
402 Google Android - 4 edio
fragnent
android:id="@+id/nav_drawer_fragnent"
android:nane="livroandroid.lib.fragnent.NavigationDrawerFragnent"
android:layout_width="@dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start" />
</android.support.v4.widget.DrawerLayout
A classe DrawerLayout responsvel por abrir o menu deslizante, por isso ela foi
denida como a raiz deste layout. Essa classe contm os mtodos openDrawer() e
closeDrawer(), que podemos utilizar no cdigo para abrir e fechar o menu.
No layout do arquivo XML, so denidos dois blocos principais. Na parte supe
rior denido o contedo do aplicativo, onde inclu a Toolbar, e na parte inferior
existe um FrameLayout que ficar vazio por enquanto, pois ser utilizado para incluir
os fragments ao selecionar algum item do menu.
Na parte inferior do layout, foi adicionado o fragment NavigationDrawerFragment, que
faz parte da biblioteca android-utils. Vamos utilizar essa classe da biblioteca para
facilitar a criao do Navigation Drawer. Uma vez que alteramos o arquivo de
layout, vamos atualizar o cdigo-fonte da activity conforme demonstrado a seguir.
MainActivity.java
public class MainActivity extends BaseActivity {
private NavigationDrawerFragment nNavDrawerFragnent;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setUpToolbar();
// Nav Drawer
nNavDrawerFragnent = (NavigationDrawerFragnent) getSupportFragnentManager()
.ndFragnentById(R.id.nav_drawer_fragnent);
// Congura o drawer layout
f3WfL3YUf dfW@FLy0ut = (DrawerLayout) ndViewById(R.id.drawer_layout);
nNavDrawerFragnent.setUp(drawerLayout);
}
Para criar a lista no menu lateral, vamos fazer a activity implementar a interface
NavigationDrawerFragment.NavigationDrawerCallbacks. Essa interface utilizada na bi
blioteca android-utils para que a activity possa retornar a view do menu lateral.
Portanto, altere o cdigo-fonte da classe MainActivity conforme demonstrado a
seguir. Veja que coloquei comentrios para explicar o signicado de cada mtodo.
MainActivity.java
public class MainActivity extends BaseActivity inplenents
NavigationDrawerFragnent.NavigationbrawerCallbacks {
@Override
public NavigationDrawerFragment.NavDrawerListView
getNavDrawerView(NavigationDrawerFragment navigationDrawerFragnent,
Layoutlnater layoutlnater, ViewGroup container) {
// Deve retornar a view e o identicador do Listview
return null;
}
@0verride
public ListAdapter getNavDrawerListAdapter(NavigationDrawerFragment navigationDrawerFragment) {
// Este mtodo deve retornar o adapter que vai preencher o Listview
return null;
}
@0verride
404 Google Android - 4 edio
int position) { ,
public void onNavDrawerItemSelected(NavigationDrawerFragment navigationDrawerFragment,
Dica: nos cdigos do livro, utilizo a notao de trs pontinhos (. . .). O objetivo
mostrar apenas a parte nova do cdigo-fonte. Os trs pontinhos indicam que
o restante do cdigo-fonte no deve ser alterado.
Por enquanto, deixaremos estes trs mtodos vazios e vamos preench-los medida
que criarmos os arquivos. Comeamos criando o arquivo XML de layout da view
que vai aparecer no menu lateral. Essa view deve conter um Listview, conforme
demonstrado a seguir:
/res/layout/nav_drawer_Iistview.xm|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id:"@+id/listViewContainer" android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="match_parent" >
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="1" android:background="#ffffff" />
<TetView
android:layout_width="match_parent" android:layout_height="50dp"
android:background="@color/gray" android:gravity="center"
android:text="Livro Android - Todos os direitos reservados"
android:tetColor="?attr/colorPrimary" />
Veja que, no cdigo deste arquivo de layout, criei o Listview e um Textview com um
texto sobre direitos reservados. Com esse arquivo pronto, podemos implementar o
mtodo getNavDrawerView( . . .) da interface NavigationDrawerCallbacks, o qual deve inflar
o layout para criar a view, e retornar a view e o identificador do Listview. Dessa
forma, a biblioteca android-utils vai saber como encontrar o Listview no layout.
MainActivity.java
public class MainActivity . . . {
@Override
Se voc executar o projeto agora e abrir o menu lateral, ver que esse layout j foi
utilizado, porm o Listview est vazio. Para preencher o Listview, vamos precisar de
um objeto que contenha o ttulo e a gura de cada item do menu, portanto crie
a classe NavDrawerMenuItem no pacote adapter.
NavDrawerMenu|tem.java
package br.com.livroandroid.carros.adapter;
public class NavDrawerMenuItem {
// Titulo: R.string.xx
public int title;
// Figura: R.drawable.x
public int img;
// Para colocar um fundo cinza quando a linha est selecionada
public boolean selected;
public NavDrawerMenuItem(int title, int img) {
this.title = title;
this.img = img;
}
// Cria a lista com os itens de menu
public static List getList() {
List list = new ArrayList();
list.add(new NavDrawerMenuItem(R.string.carros, R.drawable.ic_drawer_carro));
list.add(new NavDrawerMenuItem(R.string.site_livro, R.drawable.ic_drawer_site_livro));
list.add(new NavDrawerMenuItem(R.string.conguracoes, R.drawable.ic_drawer_settings));
return list;
}
/res/values/strings.xmI
<'eSOU'CeS>
<string name="carros">Carros
<string name="site_livro">Site do Livro</5F9>
<string name:"conguracoes">Conguraes
406 Google Android - 4 edio
O arquivo de layout do adapter pode ser visualizado a seguir. Ele contm apenas
o Imageview do cone e o Textview para o ttulo do item de menu.
/res/layout/adapter_nav_drawer.mI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout nlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="48dp"
android:background="@color/white" android:gravity="center_vertical"
android:orientation="horizontal" \
android:paddingLeft="16dp" android:paddingRight="16dp">
<ImageView android:id="@+id/ing"
android:layout_width="24dp" android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_drawer_carro" />
<TetView android:id="@+id/text"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:text="Menu"
android:textAppearance="?android:attr/textAppearanceedium"
android:textColor="@color/black" android:textSize="14sp" />
A seguir, temos o cdigo da classe do adapter, que vai preencher o Listview. Voc
pode cri-la no pacote adapter.
NavDrawerMenuAdapter.java
package br.com.livroandroid.carros.adapter;
// imports
public class NavDrawerMenuAdapter extends BaseAdapter {
protected static nal String TAG = "livroandroid";
private nal List list;
private nal Context context;
private Layoutlnater inater;
public NavDrawerMenuAdapter(Context context, List list) {
this.context = context;
this.list = list;
this.inater = (Layoutlnater) Layoutlnater.from(context);
}
@Override
public int getCount() { _
return list != null ? list.size() : 0;
}
@0verride
Captulo 13 I Navigation Drawer
@Override
@0verride
// Atualiza a view
NavDrawerMenuItem item = list.get(position);
holder.tet.setTet(item.title);
holder.img.setImageResource(item.img);
if (item.selected) {
// Congura o fundo cinza do item selecionado.
view.setBackgroundResource(R.drawable.seletor_nav_drawer_selected);
holder.text.setTetColor(context.getResources().getColor(R.color.primary))
} else {
view.setBackgroundResource(R.drawable.seletor_nav_drawer);
holder.tet.setTetColor(contet.getResources().getColor(R.color.black));
}
return view;
}
notfyDataSetChanged();
}
O cdigo da classe do adapterf extenso, mas acredito que voc j consiga entend-lo.
O adapter no apenas est inando seu arquivo de layout para criar a view como
tambm utiliza o padro Vewl-Iolder para reaproveitar as views durante a rolagem. Ob
serve que tambm coloquei os mtodos setSelected() e clearSelected() para controlar o
item de menu que est selecionado, pois uma boa prtica mostrar um fundo com
alguma cor diferenciada quando algum item est selecionado. Justamente por causa
desse fundo, precisamos criar os arquivos de seletores conforme demonstrado a seguir:
/res/drawable/seIetor_nav_drawer.mI
<selector mlns:androd="http://schemas.androd.com/apk/res/androd">
<tem androd:drawable="@color/transparent" androd:state_selected="true" />
<tem androd:drawable="@color/transparent" androd:state_pressed="true" />
<item android:drawable="@color/white" androd:state_selected="false" /
/res/drawabIe/seIetor_nav_drawer_se|etted.xmI
<selector mlns:android="http://schemas.androld.com/apk/res/androd">
<tem androd:drawable="@color/transparent" androld:state_selected="true" />
<tem androd:drawable="@color/transparent" androd:state_pressed="true" />
<tem androd:drawable="@color/gray" androld:state_selected="false" />
Nota: um seletor (selector) um arquivo XML que deve car na pasta /res/drawablc.
Pa ra cada estado, como selecionado (selected) ou pressionado (pressed), podemos
definir 0 fundo f1CSS?1fl0, Seja com imagens ou corcs. Neste caso, estou
denindo a cor de fundo cinza quando o item est selecionado.
@0verride
public ListAdapter getNavDrawerListAdapter(NavigationDrawerFragnent
navigationDrawerFragment) {
List list = NavDrawerMenuIten.getList();
// Deixa o primeiro item selecionado
list.get(0).selected = true;
this.listAdapter = new NavDrawerMenuAdapter(this, list);
return listAdapter;
1
Terminada essa alterao, execute o projeto novamente. Dessa vez, o menu lateral
vai mostrar a`lista com as opes, conforme a gura 13.8.
!| site do Livro
Q Configuraes
'\ 1 "
1 'N-i'.iC'
l
Nota: se tivssemos utilizado a action bar, o menu lateral seria aberto por
debaixo da barra. Como utilizamos a Toolbar e ela uma view, o menu lateral
consegue abrir por cima da Toolbar, o que um padro do Material Design.
Mesmo assim, ele no abriu por cima da status bar (barra superior que mostra
as horas), mas isso vamos ver depois.
@0verride
public void onNavDrawerItemSelected(NavigationDrawerFragment navigationDrawerFragment,
int position) {
List<NavDrawerMenuIten list = Navbrawerenulten.getList();
Navbrawerenulten selectedlten = list.get(position);
]/ Seleciona a linha
this.listAdapter.setSelected(position, true); \
toast("Clicou no item: " + getString(selectedItem.title));
}
Por padro, o Android Studio cria um item de menu com o texto Settings. Vamos
alterar esse arquivo de menu para criar um item chamado Sobre (About).
i /res/menu/menu_main.mI
<menu mlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<item
android:id="@+id/action_about" android:title="@string/action_about"
android:orderInCategory="100" app:showAsAction="never" />
/res/values/strings.xmI
<'eSOU'CeS>
<string name="action_about">Sobre</gtrng>
@0verride
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInater().inate(R.menu.menu_man, menu);
return true;
}
@Override
public boolean on0ptonsItemSe1ected(Menultem item) {
int id = item.getItemId();
if (id == R.d.acton_about) {
toast("C1icou no Sobre");
return true;
}
return super.on0ptonsItemSe1ected(tem) ;
}
Ao clicar no menu Sobre, um toast ser exibido (Figura 139). Posteriormente vamos me
lhor esta tela e criar um Fragnentalog com a descrio do projeto. Essa funcionalidade
bem comum nos aplicativos para que o usurio obtenha informaes adicionais.
Chcou no Sobre
/res/values/styIes.xmI
<item name:"windowActionBar0ver1ay">true
/res/values-v21/styles.xmI
<`ESOU`CS>
AndroidManifest.mI
<manifest _ . .>
<app1ication . . . androd:theme="@style/AppTheme" >
<activity
android:name=".activty.ManActyty" android:thene="@sty1e/AppThene.NavDrawer" . _ . />
</app1cation>
Se voc executar o projeto agora, ver que o menu lateral j ocupou a tela inteira.
Mas, por causa dessa alterao, a barra de status est transparente, e precisamos
desenhar a cor dela manualmente. Isso feito com uma nica linha de cdigo,
conforme demonstrado a seguir:
MainActivity.java
Basicamente o que fizemos foi deixar a barra de status transparente, para que o
contedo da tela seja desenhado por cima dessa barra. Isso foi possvel porque
customizamos as propriedades do tema.
A figura 13.10 mostra o resultado. Veja que a Toolbar est deslocada para cima e
invadindo o espao da barra de status (system bar). Ao abrir o menu lateral, veja
que ele abre por cima de tudo (inclusive da Toolbar e da status bar), o que est
correto no Material Design.
O problema que, quando o menu do Navigation Drawer estiver fechado, no
queremos que a Toolbar seja deslocada para cima. Para resolver esse problema, o
Google criou a classe ScrmInsetsFrameLayout que faz parte do aplicativo do Google l/O
2014, cujo cdigo-fonte est disponvel no Gitl-Iub: https://github.com/google/ioschcd.
41 4 Google Android - 4 edio
!| sn. ao Livro
Confqurn
LEE /res/layout/activity_main.xm|
<androd.support.v4.wdget.DrawerLayout xm1ns:androd="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.androd.con/apk/res-auto"
androd:td="@+d/drawer_1ayout"
androtd:1ayout_wdth="match_parent"
androd:1ayout_heght="match_parent"
androd:tsSystemHndows="true">
<LinearLayout ... >
</LnearLayout>
<com.goog1e.sanp1es.apps.iosched.ui.wdget.ScrimInsetsFrameLayout
androd:id="@+id/contanerScrmlnsets"
android:1ayout_width="wrap_content" android:1ayout_heght="match_parent"
android:1ayout_gravty="start" androd:e1evaton="8dp"
android:tsSystemHndows="true" app:insetForeground="#4000"
<fragment
android:id:"@+id/nav_drawer_fragment"
androd:name="1ivroandrod.1b.fragment.NavgatonDrawerFragment"
androd:layout_wdth="@dmen/navgation_drawer_wdth"
androd:1ayout_height="match_parent"
androd:1ayout_gravity="start" />
</com.goog1e.samp1es.apps.iosched.ui.widget.ScrnInsetsFrameLayout>
</androd.support.v4.widget.DrawerLayout>
Captulo 13 I Navigation Drawer 415
O atributo androd:fitsSystemwndows="true" que foi colocado na raiz do layout faz
com que o layout seja desenhado mais para baixo, encaixando-se na janela que
o sistema utiliza para desenhar a aplicao. Ao fazer isso, o Navigation Drawer
tambm vai abrir mais para baixo, porm no caso do menu queremos manter ele
abrindo sobre a barra de sistema, pois esse o padro do Material Design. justa
mente por isso, o fragment que controla o Navigation Drawer foi envolvido com
a classe ScrmInsetsFrameLayout do Google, que por sua vez faz a mgica de desenhar
o Navigation Drawer por cima de tudo. Esse o pulo do gato!
Ao executar o projeto, o resultado deve ser como a gura 13.11.
!| site ao Livro
Q Configuraes
/res/layout/nav_drawer_Iistview.mI
<LinearLayout mlns:androd="http://schemas.android.com/apk/res/androd"
android:id:"@+id/listViewContaner" androd:orentation="vertica1"
androd:1ayout_wdth="natch_parent" android:1ayout_heght="match_parent" >
<nc1ude 1ayout="@1ayout/nav_drawer_1stvew_header" /
LstVew
androd:id="@+id/Iistvew"
416 Google Android - 4' edio
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="1" android:background="#ffffff" />
<TetView . . .
android:tet="Livro Android - Todos os direitos reservados" />
!| Site do Livro
Q Configuraes
Para fechar o assunto com chave de ouro, vamos adicionar uma linha de cdigo no
metodo getNavDrawerView(), o qual responsvel por criar a view do menu lateral. O
que vamos fazer e simplesmente preencher o layout do cabealho com a foto (logol
do aplicativo ou usurio, e algumas informaes adicionais como nome e email.
fs ManActivity.java
@0verride
public NavigationDrawerFragment.NavDrawerListView getNavDrawerView(. . .) {
View view = layoutlnater.inate(R.layout.nav_drawer_listview, container, false);
// Preenche o cabealho com a foto, nome e email.
navigationDrawerFragment.setHeaderValues(view, R.id.listViewContainer,
R.drawable.nav_drawer_header, R.drawable.ic_logo_user, R.string.nav_drawer_usernane
R.string.nav_drawer_enail);
return new NavigationDrawerFragment.NavDrawerListView(view,R.id.listView);
}
}
Captulo 13 I Navigation Drawer 417
Dica: na pgina do Android Design, no tpico sobre o Navigation Drawer, podemos
encontrar a especificao do layout para esse cabealho. As boas prticas de
interface denem mtricas de espaamentos, alinhamentos, fontes etc.
/res/values/strings.xmI
<I'SOUI`CES>
l Carros
E] Site do Livro
Q Conguraes
CarrosFragment.java
package br.com.livroandroid.carros.fragments;
public class CarrosFragment extends BaseFragnent {
@0verride
public View onCreateView(Layoutlnater inater, Viewroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_carros, container, false);
return view;
}
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="Lista de Carros Aqui" android:layout_gravity="center" />
Vamos criar tambm o fragment para abrir a pgina do livro com um WebVievv
SiteLivroFragment.java
package br.con.livroandroid.carros.fragnents;
public class SiteLivroFragnent extends BaseFragnent {
@Override
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_site_livro, container, false);
return view;
}
/res/layout/fragment_site_Iivro.xmI
<FraneLayout xnlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="natch_parent android:layout_height="natch_parent">
<TetView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Pgina do Livro Aqui" android:layout_gravity="center" />
Ainda no vamos fazer nada com o item de menu configuraes, pois vamos
estudar essa parte no captulo 18, sobre persistncia.
Depois de criar os fragments, vamos adiciona-los dinamicamente no layout da
activity principal. Para isso, altere o mtodo onNavDrawerItenSelected(. . .) da MainActivity.
Por enquanto esse mtodo mostra um toast com o ttulo do menu selecionado, mas
desta vez, ao selecionar um item, vamos adicionar o fragment no layout.
420 Google Android - 4' edio
MainActivty.java
inport android.support.v4.app.Fragnent;
public class HainActivity extends BaseActivity implenents
NavigationDrawerFragment.NavigationDrawerCallbacks {
@0verride
public void onNavDrawerItenSelected(NavigationDrawerFragment navigationDrawerFragment,
int position) {
ListNavDrawerHenuIten> list = Navbrawerenulten.getList();`
Navbrawerenulten selectedlten = list.get(position);
// Seleciona a linha i
this.listAdapter.setSelected(position, true);
if (position == 6) {
replaceFragnent(new CarrosFragnent());
} else if (position == 1) {
replaceFragnent(new SiteLivroFragnent());
} else if (position == 2) {
toast("Conguraes");
} else {
Log.e("livroandroid", "Item de menu invlido");
}
}
// Adiciona o fragnent no centro da tela
private void replaceFragment(Fragnent frag) {
getSupportFragnentHanager().beginTransaction().replace(R.id.nav_drawer_container,
frag, "TAG").connit();
}
Veja que criei o mtodo replaceFragmetn(frag) para facilitar a leitura do cdigo, e preste
ateno pois voc precisa importar a classe da biblioteca de compatibilidade, que
android.support.v4.app.Fragment. Veja que a FragmentTransaction est fazendo replace()
e no add(), pois sempre queremos substituir o fragment do contedo por outro.
Ao terminar essas conguraes, execute o projeto novamente, e o resultado deve
ser como a gura 13.14, que mostra o fragment dos carros. Na parte da esquerda
da gura podemos ver o menu aberto, e na direita o fragment que foi adicionado
COH1OCOnRdO3OChGHIK)HWU(m&
O item de menu Site do Livro vai adicionar seu respectivo fragment na tela, e o item
(onguraes vai mostrar um simples toast por enquanto.
Captulo 13 n Navigation Drawer 421
E1 sned Livro
Conguraes
Neste captulo, criamos o projeto dos carros, e talvez este tenha sido o passo mais
difcil, pois geralmente o complicado comear.
Aprendemos a utilizar o padro de navegao do Navigation Drawer, e para con
tinuar os seus estudos separei alguns links da documentao ocial.
Android Developer- Patterns - Navigation Drawer
https://developerandroid.com/design/patterns/navigation-drawen html
Material Design - Navigation Drawer
http://wwwgoogle.com/design/spec/patterns/navigation-drawex html
https://developer android.com/training/basics/actionbar/overlaying.html
"* i CAPTULO 14
~l
WebView
*M 1
\
1
\
Neste captulo, vamos estudar detalhes sobre a classe Hebvew, um dos componentes
mais utilizados e flexveis do Android.
/res/layout/fragment_site_Iivro.xmI
<?m1 verson="1.G" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/androd"
android:1ayout_width="match_parent" androd:1ayout_heght="match_parent">
<webView androd:d="@+id/webview"
android:1ayout_width="match_parent" androd:layout_height="match_parent" />
<ProgressBar android:id="@+d/progress"
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content"
android:1ayout_gravity="center" /
Observe que o layout raiz um FrameLayout para deixar o ProgressBar por cima dO
webview. No cdigo do fragment basta mostrar a pgina no webvew.
SiteLvroFragment.java
package br.com.1ivroandroid.carros.fragments;
422
Captulo 14 I WebView 423
private static nal String URL_SOBRE = "http://www.livroandroid.con.br/sobre.htm";
private webview webview;
private ProgressBar progress;
@Override
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_site_livro, container, false);
webview = (WebView) view.ndViewById(R.id.webview);
progress = (ProgressBar) view.ndViewById(R.id.progress);
setNebViewClient(webview);
// Carrega a pgina
webview.loadUrl(URL_SOBRE);
return view;
}
});
}
AndroidManifest.xmI
<manifest . . .>
uses-permission android:nane="android.permission.INTERNET" /
<application . . . />
Feito isso, execute o projeto, e o resultado deve ser como a gura 14.1. Acredito
que seja simples entender esse exemplo, pois j estudamos o webview no captulo 7,
sobre views. Sendo assim, nos prximos tpicos vamos explorar algumas funcio
nalidades mais avanadas.
424 Google Android - 4 edio
1 1I 1z1
1: Livro 1.
i
1
1 Google
i Andnd Bau
FIDROID
^'""""":2fl'.'..'
1
V' F .z " WW'
\
1 Este livro dedicado a todos os q
desenvolvedores Android, desde quem
est aprendendo pela primeira vez, at 1 ;
1 quem est buscando aprimorar seus 1 1
p conhecimentos do
funcionalidades e estudar
Androidas 5.0
novas
e 11pl
1 Material Design.
Mais informaes Z
Figura 14.1 - Pgina do livro com Webl/ieu
/res/layout/fragment_site_Iivro.xmI
<?m1 version="1.G" encodng="utf-8"?>
<FrameLayout . . .>
androd.support.v4.widget.SwpeRefreshLayout
androd:id="@+d/swpeToRefresh"
androd:1ayout_width="match_parent" androd:1ayout_height="match_parent">
<NebVew . . . />
</android.support.v4.wdget.SwpeRefreshLayout
<ProgressBar . . . />
SiteLivroFragment.java
// Swipe to Refresh
swipeLayout = (SwipeRefreshLayout) view.ndViewById(R.id.swipeToRefresh);
swipeLayout.set0nRefreshListener(0nRefreshListener());
swipeLayout.setColorScheneResources(
R.color.refresh_progress_1,
R.color.refresh_progress_2,
R.color.refresh_progress_3);
1t
return view;
}
};
@Override
public void onPageFinished(webView webview, String url) {
// Desliga o progress
progress.setVisibility(View.INVISIBLE);
// Termina a animao do Swipe to Refresh
swipeLayout.setRefreshing(false);
}
});
}
}
426 Google Android - 4 edio
Para o cdigo compilar, crie as cores da animao do SwDER@ff@S|1|-6YU'f CIUC Uma
bolinha girando que ca alternando entre as cores que VOCE! Cllf
i /res/values/colors.xml
<fESOUl'CS>
<color name="refresh_progress_1">#33B5E5
<color name="refresh_progress_2">#99CCGG/color> \
<color name="refresh_progress_3"#CC00@G
</resources
Livro
Google )
Android
_.i\.;'Q_==_';>.L1.>
..-u-puv-:_v.-.~_
Dica: para fazer o gesto Pull to Refresh, toque na lista e segure, depois arraste para
baixo e solte. o mesmo gesto utilizado em aplicativos nativos como o Gmail.
Nesta pgina existe um link Mais Informaes que est chamando a prpria pgina
sobre.htm novamente. Nosso objetivo interceptar o clique nesse link para mostrar
um alerta com algumas informaes adicionais de forma nativa no aplicativo. Isso
Captulo 14 I Weblew 427
simples de fazer, basta sobrescrever o mtodo should0verrideUrlLoading(view,url)
da classe webViewClient.
SiteLivroFragment.java
public class SiteLivroFragment extends BaseFragment {
@0verride
public boolean should0verrideUrlLoading(lrlebview view, String url) {
Log.d("livroandroid", "webview url: " + url);_
if (url != null && url.endswith("sobre.htm")) {
toast("Clicou em Mais Informaes");
// Retorna true para.informar que interceptanos o evento
return true;
1
});
}
/res/values/strings.xmI
< ESOU l'CS>
android:id="@androd:id/tet1"
/res/layout/diaIog_about.xmI
<TetVew xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:1ayout_height="match_parent"
android:paddng="24dp" android:text:"@string/about_da1og_tet" />
</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_10' title="bbox 548 1159 1499 1224"><span class='ocr_word' id='word_1_40' title="bbox 548 1168 879 1224"><span class='xocr_word' id='xword_1_40' title="x_wconf -4"><b>Ap1icatvo</span></span> <span class='ocr_word' id='word_1_41' title="bbox 908 1166 981 1212"><span class='xocr_word' id='xword_1_41' title="x_wconf -1">dos</span></span> <span class='ocr_word' id='word_1_42' title="bbox 1011 1168 1161 1210"><span class='xocr_word' id='xword_1_42' title="x_wconf -2">Carros</span></span> <span class='ocr_word' id='word_1_43' title="bbox 1193 1159 1499 1216"><span class='xocr_word' id='xword_1_43' title="x_wconf -4">2015</b><br></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_11' title="bbox 548 1253 983 1302"><span class='ocr_word' id='word_1_44' title="bbox 548 1261 700 1302"><span class='xocr_word' id='xword_1_44' title="x_wconf -1">Versao</span></span> <span class='ocr_word' id='word_1_45' title="bbox 728 1253 983 1301"><span class='xocr_word' id='xword_1_45' title="x_wconf -2">%s<br><br></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_12' title="bbox 550 1321 2602 1396"><span class='ocr_word' id='word_1_46' title="bbox 550 1357 597 1389"><span class='xocr_word' id='xword_1_46' title="x_wconf -1"><a</span></span> <span class='ocr_word' id='word_1_47' title="bbox 629 1321 2602 1396"><span class='xocr_word' id='xword_1_47' title="x_wconf -4">href="http://www.1vroandrod.com.br">http://lvroandrod.com.br</a><br><br></span></span></span>
<span class='ocr_line' id='line_1_13' title="bbox 551 1408 2910 1484"><span class='ocr_word' id='word_1_48' title="bbox 551 1444 597 1476"><span class='xocr_word' id='xword_1_48' title="x_wconf -1"><a</span></span> <span class='ocr_word' id='word_1_49' title="bbox 626 1408 2910 1484"><span class='xocr_word' id='xword_1_49' title="x_wconf -5">href="http://www.facebook.com/rcardo1echeta">http://facebook.com/ricardo1echeta</a><br><br</span></span></span>
<span class='ocr_line' id='line_1_14' title="bbox 552 1496 2908 1570"><span class='ocr_word' id='word_1_50' title="bbox 552 1531 598 1563"><span class='xocr_word' id='xword_1_50' title="x_wconf 0"><a</span></span> <span class='ocr_word' id='word_1_51' title="bbox 629 1496 2908 1570"><span class='xocr_word' id='xword_1_51' title="x_wconf -5">href="https://plus.goog1e.com/+RcardoLecheta">https://plus.goog1e.com/+RcardoLecheta</a</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_15' title="bbox 647 1602 717 1659"><span class='ocr_word' id='word_1_52' title="bbox 647 1602 717 1659"><span class='xocr_word' id='xword_1_52' title="x_wconf -2">
AboutDiaIog.java
package br.com.1vroandrod.carros.fragments;
import androd.app.A1ertDa1og;
Captulo 14 I WebView 429
import android.app.Dialog;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import livroandroid.lib.utils.AndroidUtils;
ft.addToBackStack(null);
new AboutDialog().show(ft, "dialog_about");
}
@0verride
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Cria o HTML com o texto de about
SpannableStringBuilder aboutBody = new SpannableStringBuilder();
String versionName = AndroidUtils.getVersionName(getActivity());
aboutBody.append(Html.fromHtml(getString(R.string.about_dialog_text, versionName)))
// Ina o layout
Layoutlnater inater = Layoutlnater.from(getActivity());
Textview view = (TetView) inater.inate(R.layout.dialog_about, null);
view.setTet(aboutBody);
view.setMovementMethod(new LinkMovementMethod());
// Cria o dialog customizado
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.about_dialog_title)
.setView(view)
.setPositiveButton(R.string.ok,
new Dialoglnterface.0nClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
)
.create();
}
}
430 Google Android - 4= edio
Veja que estamos utilizando a classe android.support.v4.app.DialogFragnent do pacote
de compatibilidade c no a classe nativa andjroid.app.DialogFragnent. Tambm estou
utilizando a classe livroandroid.lib.utils .AndroidUtils da biblioteca android-utils para
retornar o cdigo da verso do aplicativo, que o atributo versionName cadastrado
no app/buildgradle. Depois de criar o Fragmentialog, vamos atualizar o cdigo da
classe SiteLivroFragment; assim, quando 0 usurio clicar no link Mais Informaes da
pgina, o evento ser interceptado para abrir o alerta.
SiteLivroFragment.java
@0verride
public boolean should0verrideUrlLoading(webview view, String url) {
Log.d("livroandroid", "webview url: " + url);
if (url != null && url.endsHith("sobre.htn")) {
AboutDialog.showAbout(getFragnentHanager());
}
O resultado podemos conferir na gura 143. Veja que o alerta mostrou o ttulo
livro Android e o boto OK que fecha a janela. Se voc clicar no boto voltar, a janela
tambm ser fechada, pois o fragment foi adicionado na back stack. Observe
que no cdigo-fonte utilizei a classe Html.fromHtml(string) para mostrar um cdigo
HTML dentro do Textview. Inclusive o usurio pode clicar nos links para visualizar
a pgina no browser.
Livro Android
I Y r
ljflu "ll\1f(j.n 1j V I
ll1
OK
MainActivty.java
Feito isso, qualquer cdigo JavaScript vai funcionar no webview e inclusive voc
poder injetar cdigos JavaScript dentro do webview, como por exemplo:
webView.loadUrl( "javascript:alert( 'Ol' " );
Eu no recomendo esse tipo de tcnica, mas j houve casos em que precisei alterar
o CSS da pgina web ( qual eu no tinha acesso ao servidor) e z isso injetando
um JavaScript na pgina.
SiteLivroFragment.java
@Override
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) [
View view = inater.inate(R.layout.fragnent_site_livro, container, false);
webview = (webview) view.ndViewById(R.id.webview);
congJavaScript();
return view;
}
Dica: a partir do Android 4.3 (Jelly Bean), somente os mtodos que forem anotados
com @JavascriptInterface sero expostos ao webview. Isso feito por questes de
segurana, pois antigamente a pagina podia chamar qualquer metodo.
, _ Cl
Isso signica que na p inaemos
g po web usar
d o objeto "LivroAndroid" para exe
cutar os metodos que esto denidos na classe LivroAndroidInterface, pois isso
que essa linha de cdigo faz.
webview.addJavascriptInterface(new LivroAndroidInterface(), "LivroAndroid");
56 VOC Olhar 0 codigo-fonte da pgina sobre htm na pgina do livro ver ue ele
esta chamando o seguinte JavaScript ao clicar na gura:
CaPtuIo 14nWebView 433
<5Cl`D'C 'CllDe="text/javascript">
function sobre() {
// Funo JavaScript que chama o mtodo na Activity
LivroAndroid . sobre( );
}
http://developer android.com/reference/android/webkit/WebWew.html
Android Developers Blog - Using DiaIogFragments
http://android-developers.blogspot.com.br/2012/05/using-dialogfragments.html
DialogFragment API
http://developer android.com/reerence/android/app/DialogFragment.html
Y i* CAPTULO, 15
z- 41
RecycIerView e tabs
H
pl
Neste captulo, vamos criar a lista de carros e aproveitar para revisar os conceitos
do Material Design. O objetivo gui-lo passo a passo no desenvolvimento de
um aplicativo Android. Deixaremos toda a estrutura da lista pronta para quando
formos buscar os carros do web service.
Para continuar o projeto dos carros, vamos criar a classe Carro, mas antes crie o pacote
br .com.livroandroid . carros .domain com o objetivo de armazenar as classes de domnio Uma
maneira de fazer isso clicar com o boto direito no pacote br.com.livroandroid.carros
e usar o menu New> Padtage; depois digite domain no nome do pacote.
Feito isso, crie as classes Carro e CarroService neste pacote, conforme a gura 15.1.
Carro.java
package br.com.livroandroid.carros.domain;
import java.io.Serializable;
public class Carro implements Serializable {
private static nal long serialVersionUID = 66G10667668324739S9L;
public long id;
public String tipo;
public String nome;
public String desc;
public String urlFoto;
public String urllnfo;
public String urlvideo;
public String latitude;
public String longitude;
@0verride
434
(Ptu|o 15 n RecycIerView e tabs 435
public String toString() {
return "Carro{" + "nome='" + nome + '\" + '}';
}
Dica: para gerar o mtodo toString() de uma classe, utilize o menu (ode > Generate
> t0String() e escolha os atributos que voc deseja imprimir. Esse mesmo wizard
apresenta opes para gerar construtores e mtodos do tipo getters e setters e
tambm pode ser invocado pela tecla de atalho Ctrl+Insert.
CarroService.java
package br_cormlivroandroid.carros.domain;
return carros;
}
}
4% Google Android - 4 edio
15.2 Criando a lista de carros
Para criar a lista de carros, utilizaremos o Recyclervew e Cardvew que j estudamos,
Portanto vamos ser rpidos, pois queremos entrar logo nas partes mais interes_
santes do projeto.
Para comear, declare a dependncia das bibliotecas, inclusive do Picasso
(https://github.com/square/picasso), que uma excelente biblioteca utilizada para o
download de imagens.
app/build.gradIe
Compile 'com.android.support:recyclervew-v7:22.1.0'
compile 'com.androd.supportzcardvew-v7:22.1.0'
comple 'com.squareup.pcasso:picasso:2.5.2'
Como nosso objetivo criar a lista de carros, vamos comear pelo layout do
adapter, que tem um Textvew para o nome e um Imagevew para a foto.
/res/layout/adapter_tarro.xmI
<?ml version="1.0" encodng="utf-8"?>
android.support.v7.wdget.CardView xmlns:androd="http://schemas.androd.com/apk/res/android"
m1ns:card_vew="http://schemas.androd.com/apk/res-auto"
androd:d="@+d/card_vew"
androd:1ayout_wdth="match_parent" android:layout_heght="match_parent"
androd:1ayout_margn="6dp" androd:c1ckable="true"
card_vew:cardBackgroundCo1or="@co1or/white" card_vew:cardCornerRadius="2dp"
card_vew:cardE1evation="6dp"
android:foreground="?attr/selectableltenackground">
<LnearLayout
androd:1ayout_wdth="natch_parent"
androd:1ayout_heght="?androd:attr/1stPreferredItemHeght"
androd:gravty="center_vertcal" android:orientation="hortzonta1">
<FrameLayout
androd:1ayout_wdth="wrap_content" androd:layout_heght="wrap_content">
</span></span> <span class='ocr_word' id='word_1_125' title="bbox 955 3383 1051 3425"><span class='xocr_word' id='xword_1_125' title="x_wconf -1">Foto</span></span> <span class='ocr_word' id='word_1_126' title="bbox 1080 3381 1128 3426"><span class='xocr_word' id='xword_1_126' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_127' title="bbox 1158 3395 1282 3428"><span class='xocr_word' id='xword_1_127' title="x_wconf -2">carro</span></span> <span class='ocr_word' id='word_1_128' title="bbox 1316 3400 1385 3424"><span class='xocr_word' id='xword_1_128' title="x_wconf -1">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 825 3467 1639 3530"><span class='ocr_word' id='word_1_129' title="bbox 825 3467 1078 3519"><span class='xocr_word' id='xword_1_129' title="x_wconf -4"><ImageView</span></span> <span class='ocr_word' id='word_1_130' title="bbox 1107 3467 1639 3530"><span class='xocr_word' id='xword_1_130' title="x_wconf -4">androd:d="@+d/img"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 916 3551 2464 3626"><span class='ocr_word' id='word_1_131' title="bbox 916 3551 1627 3614"><span class='xocr_word' id='xword_1_131' title="x_wconf -4">android:1ayout_wdth="116dp"</span></span> <span class='ocr_word' id='word_1_132' title="bbox 1661 3563 2381 3625"><span class='xocr_word' id='xword_1_132' title="x_wconf -4">androd:1ayout_height="50dp"</span></span> <span class='ocr_word' id='word_1_133' title="bbox 2417 3570 2464 3626"><span class='xocr_word' id='xword_1_133' title="x_wconf -1">/</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 825 3638 2136 3705"><span class='ocr_word' id='word_1_134' title="bbox 825 3638 918 3678"><span class='xocr_word' id='xword_1_134' title="x_wconf -1"><!--</span></span> <span class='ocr_word' id='word_1_135' title="bbox 952 3639 1076 3682"><span class='xocr_word' id='xword_1_135' title="x_wconf -2">Barra</span></span> <span class='ocr_word' id='word_1_136' title="bbox 1106 3638 1153 3682"><span class='xocr_word' id='xword_1_136' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_137' title="bbox 1184 3652 1410 3694"><span class='xocr_word' id='xword_1_137' title="x_wconf -2">progresso</span></span> <span class='ocr_word' id='word_1_138' title="bbox 1440 3651 1642 3699"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">enquanto</span></span> <span class='ocr_word' id='word_1_139' title="bbox 1672 3661 1848 3705"><span class='xocr_word' id='xword_1_139' title="x_wconf -1">carrega</span></span> <span class='ocr_word' id='word_1_140' title="bbox 1880 3665 1900 3697"><span class='xocr_word' id='xword_1_140' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_141' title="bbox 1933 3652 2031 3698"><span class='xocr_word' id='xword_1_141' title="x_wconf -3">foto</span></span> <span class='ocr_word' id='word_1_142' title="bbox 2067 3670 2136 3693"><span class='xocr_word' id='xword_1_142' title="x_wconf -1">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 825 3723 1897 3792"><span class='ocr_word' id='word_1_143' title="bbox 825 3723 1127 3775"><span class='xocr_word' id='xword_1_143' title="x_wconf -2"><ProgressBar</span></span> <span class='ocr_word' id='word_1_144' title="bbox 1158 3724 1897 3792"><span class='xocr_word' id='xword_1_144' title="x_wconf -5">androd:d="@+td/progresslmg"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_36' title="bbox 917 3806 2119 3876"><span class='ocr_word' id='word_1_145' title="bbox 917 3806 2119 3876"><span class='xocr_word' id='xword_1_145' title="x_wconf -4">style="@androd:style/wdget.ProgressBar.Smal1"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_37' title="bbox 916 3891 2768 3973"><span class='ocr_word' id='word_1_146' title="bbox 916 3891 1807 3958"><span class='xocr_word' id='xword_1_146' title="x_wconf -7">dmd213)/0Ut_Wdth="wrap_content"</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1842 3910 2768 3973"><span class='xocr_word' id='xword_1_147' title="x_wconf -4">androd:1ayout_height="wrap_content"</span></span></span>
</p>
</div>
</div>
</body>
</html>
Captulo 15 I RecycIerView e tabs 437
androd:1ayout_gravty="center|center_vertcal" androd:1ayout_marginRght="6dp"
androd:gravty="center|center_vertca1" androd:vsib1ity="invsb1e" />
<TextView android:d="@+d/text"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
androd:1ayout_margnLeft="10dp" android:tetCo1or="@co1or/primary" />
</LnearLayout>
/android.support.v7.wdget.CardView
/res/values/styIes.xmI
<tem name="co1orContro1Hgh1ight">@co1or/contro1_hgh1ght</tem>
/res/values/coIors.xmI
<co1or name:"contro1_high1ight">#B3E5FC
Se voc utilizou a mesma cor que est neste livro, o efeito de ripple azul claro.
Lembrando que todas as cores so baseadas na paleta de cores do Material Design.
http://www. google. com/design/spec/style/color html
A seguir podemos ver o cdigo da classe de adapter que vai utilizar o layout
/res/layout/adapter_carro.xml qu 21C21b1T105 de Criar
438 Google Android - 4= edio
Carro1\dapter.java
package br.com.livroandroid.carros.adapter;
@Override
public int getItemCount() { return this.carros != null ? this.carros.size() : 6; }
@Override
public CarrosViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = Layoutlnater.from(contet).inate(R.layout.adapter_carro, viewroup, false);
CarrosViewHolder holder = new CarrosViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(nal CarrosViewHolder holder, nal int position) {
// Atualiza a view
Carro c = carros.get(position);
holder.tNome.setText(c.nome);
holder.progress.setVisibility(View.VISIBLE);
// Faz o download da foto e mostra o ProgressBar
Picasso.with(context).load(c.urlFoto).t().into(holder.img
new com.squareup.picasso.Callback() {
@Override
public void onSuccess() {
holder.progress.setVisibility(View.GONE); // download ok
}
@Override
public void onError() {
holder.progress.setVisibility(View.GONE);
}
});
Captulo 15 I Recyc|erView e tabs 439
// Click
if (carro0nClickListener != null) {
holder.itemView.set0nClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// A varivel position nal
carro0nClickListener.onClickCarro(holder.itemview, position);
}
});
}
A esta altura do livro, o cdigo do adapter deve ser tranquilo para voc, portanto vamos
continuar. Adicione um Recyclerview no layout do fragment que vai listar os carros.
440 Google Android - 4 edio
/res/layout/fragment__carros.xmI
<?xnl version="1.0" encoding="utf-8"?> ,
<FrameLayout xnlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent"
android:padding="14dp">
android.support.v7.widget.Recyclerview android:id="@+id/recyclerview"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:cacheColorHint="@android:color/transparent"
android:clipToPadding="false"
android:divider="@null" android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"
android:scrollbarStyle="outside0verlay" android:scrollbars="vertical" /
(arrosFragment.java
package br.con.livroandroid.carros.fragnents;
@0verride
};
}
I_
I
1
1
_
,Q _. .
U =., .
..,
a
'-_ I'- | __, r *
z _
"*':;
f
Cano?
Cam) 2 Carro 2 K
`_. , , __.
`_, z~-
. Carro3 , __,
V z ,___ _' ..*Carro3
~ 1I
i_W___: __,_'_w, _____,f_ __-._-. Carro: Carro 2
Carro4 C8"4
- _ |\ ___,
app/build.gradIe
compiie 'com.android.support:design:22.2.0'
/res/layout/fragment_carros_tab.xmI
<?ml version="1.0" encoding="utf-8"?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:orientation="vertica1"
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent" android:1ayout_height="wrap_content"
android:background:"@co1or/primary" />
<android.support.v4.vieu.ViewPager android:id="@+id/viewpager"
android:layout_width="match_parent" android:1ayout_height="9p"
android:layout_weight="1" android:background="@android:color/white" />
CarrosTabFragment.java
package br.com.1ivroandroid.carros.fragments;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
Captulo 15 n Recyclerlew e tabs 443
public class CarrosTabFragment extends BaseFragment
implements TabLayout.OnTabSelectedListener {
private ViewPager mViewPager;
private TabLayout tabLayout;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_carros_tab, container, false);
// ViewPager
nViewPager = (ViewPager) view.ndViewById(R.id.viewpager);
mViewPager.setOffscreenPageLimit(2);
nViewPager.setAdapter(new TabsAdapter(getContet(), getChildFragmentManager()));
// Tabs
tabLayout = (TabLayout) view.ndViewById(R.id.tabLayout);
int cor = getContet().getResources().getColor(R.color.white);
// Cor branca no texto (o fundo azul foi denido no layout)
tabLayout.setTabTextColors(cor, cor);
// Adiciona as tabs.
tabLayout.addTab(tabLayout.newTab().setTet(R.string.classicos));
tabLayout.addTab(tabLayout.newTab().setTet(R.string.esportivos));
tabLayout.addTab(tabLayout.newTab().setTet(R.string.luxo));
// Listener para tratar eventos de clique na tab
tabLayout.set0nTabSelectedListener(this);
// Se mudar o ViewPager atualiza a tab selecionada
mViewPager.addOnPageChangeListener(new TabLayout.TabLayout0nPageChangeListener(tabLayout));
return view;
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
// Se alterar a tab, atualiza o ViewPager
mViewPager.setCurrentIten(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) { }
@0verride
public void onTabReselected(TabLayout.Tab tab) { }
}
Para prosseguir, crie a classe do adapter que vai retornar os fragments para 0
viewPager. Como as trs tabs vo mostrar a lista de carros, usaremos sempre O
fragment CarrosFragment que contm a lista, mas vamos passar um argumento pelo
Bundle para indicar o tipo de carro que ser mostrado nalista.
TabsAdapter.java
package br.com.livroandroid.carros.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class TabsAdapter extends FragmentPagerAdapter {
private Context context;
public TabsAdapter(Context context, FragmentManager fm) {
super(fm);
this.context = context;
}
@0verride
public int getCount() { return 3; }
@0verride
public CharSequence getPageTitle(int position) {
if (position == 0) {
return context.getString(R.string.classicos);
} else if (position == 1) {
return context.getString(R.string.esportivos);
}
return context.getString(R.string.luxo);
}
@0verride
/res/values/strings.xmI
<string name="classicos">Clssicos
<string name="esportivos">Esportivos
<string name="luo">Luo
CarrosFragment.java
}
W Google Android - 4 edio
private void taskCarros() {
this.carros = CarroService.getCarros(getCntet(), tipo);
CarroService.java
\
000
public static List getCarros(Context context, String tipo) {
for (int 1 = o; 1 zo; 1++`{
Carro c = new Carro();
c.none = "Carro " + tipo + ": " + i; // Nome dinmico conforme o tipo
Note que s preciso alterar uma linha nesse cdigo, apenas para o nome do
carro conter o tipo selecionado. Por enquanto, a lista de carros continuar esttica
para facilitar a construo da navegao do projeto.
Para nalizar a congurao, precisamos alterar o cdigo que trata o evento do
Navigation Drawer, para mostrar o fragment com as Tabs ao selecionar a opo Carros.
Altere o mtodo onNavDrawerItemSelected(. . .) para mostrar a classe CarrosTabFragnent
em vez da CarrosFragment, conforme demonstrado a seguir.
MainActivity.java
@0verride
public void onNavDrawerItemSelected(NavigationDrawerFragment navigationDrawerFragment,
int position) {
if (position == G) {
replaceFragnent(new CarrosTabFragment());
} else if (position == 1) { . . . }
} else if (position == 2) { . . .}
}
O resultado deve ser como a gura 153, que mostra a tab Esportivos selecionada.
Captulo 15 I RecycIerView e tabs 447
Carro esportivos: D
_ ._._e,....-._..... ,_-... a. W., .,_
Carro esportivosti
_.-.. ,~..z.,.-..._......, ..,,. ..._,._,___,- ,,._T__
Carro esportivos: 2
. Carro esportivos: 3
._......._-.T......_ .., . ...v,..:,.,._.,;.--...._T. _ T. _. ,,__",_,,___,__,
..~=n-..
CarroActivity.java
package br.com.livroandrod.carros.actvity;
public class CarroActvty extends BaseActvty {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVew(R.1ayout.actvty_carro);
448 Google Android - 4 edio
/res/layout/activity_carro.mI
<LinearLayout mlns:android="http://schemas.android.con/apk/res/android"
nlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >
include layout:"@layout/include_toolbar" /
<fragment android:id="@+id/CarroFragment"
class="br.con.livroandroid.carros.fragments.CarroFragment"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:layout:"@layout/fragment_carro" />
AndroidManifest.mI
<application . . .>
<activity android:name=".activity.CarroActivity"
android:label:"@string/title_activity_carro"
android:parentActivityName=".activity.MainActivity" >
<meta-data android:nane="android.support.PARENT_ACTIVITY"
android:value=".activity.MainActivity" />
Captulo 15 I RecycIerView e tabs
CarroFragment.java
package br.com.livroandroid.carros.fragments;
public class CarroFragment extends BaseFragnent {
private Carro carro;
@Override
/res/layout/fragment_carro.java
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.con/apk/res/android"
xnlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="natch_parent" android:layout_height="natch_parent"
android:orientation="vertical" android:padding="12dp">
<android_support.v7.widget.Cardview android:id="@+id/card_view"
android:layout_width="natch_parent" android:layout_height="match_parent'
android:clickable="true" card_view:cardBackgroundColor="@color/white"
card_view:cardCornerRadius="2dp" card_view:cardElevation="6dp">
<LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent
android:orientation="vertical" android:padding="Zdp">
<InageView and roid:id="@+d/ F19 "
android:layout_width="@dimen/foto_carro_width"
android : layout_height="@dimen/f0t0_CaFF_h@9ht"
android:layout_gravity="center" android:scaleType="center" />
<View android:background="@C010F/Qfy"
android:layout_width="match_parent" android:layout_height="1dp"
450 Google Android - 4= edio
androd:layout_nargnBottom="6dp" androd:layout_nargnTop="6dp" />
<ScrollVew
androd:layout_wdth="match_parent androd:layout_height="wrap_content"
<TetVew android:id="@+d/tDesc"
androd:layout_wdth="natch_parent" androd:layout_heght="match_parent"
androd:layout_weght="1" androd:textColor="@color/prmary_dark"
android:layout_margn="8dp"/>
</ScrollVew>
</LnearLayout>
</androd.support.v7.wdget.CardVew>
</LnearLayout>
/res/values/dimens.xm|
<I'SOUI`CS>
<dmen name="foto_carro_wdth">260dp
<dtnen nane="foto_carro_heght">110dp</dmen>
Para fazer a navegao de telas, altere o cdigo que trata o evento da lista.
CarrosFragment.java
..,. . . .
i Serro esportivos: O
z
.-v-1
.`_ ._, 4l
Carro esforivnsi
, Cam: spczzrrwosi 3
Figura 15.4 - Navegao de telas.
IO
15.5 L|nks uteis
_ - - ' ' ` ca tulo
Neste captulo, implem entamos a lista de carros e revisamos importantes con
ceitos do desenvolvimento de aplicativos para Ar1dI01d- NO Pmxlmo P
vamos buscar a lista de car ros de um Web service. Para continuar seus estudos,
separei um link.
Android Training - Creating Lists d Cafdf*
httpg-//developer android.com/training/material/lists-cards.html
` cAPruLo 16
Parser de XML, JSON e testes
`**. unitrios
1
A interface java.to. InputStream dojava dene um fluxo de dados para leitura, c COIT1
ela podemos ler facilmente o arquivo utilizando a classe java.o.F1eInputStreaI'1
BCTTI, 21 partir daqui java puro, portanto, se voc j trabalhou com arquivos em
java vai se sentir em casa.
452
Captulo 16 n Parser de XML, JSON e testes unitrios 453
<5f@*e*==~mfic i , ;a t
it <=ff=-'===i=5~* sst, ts ts .
Wmjava
: mas 1
13
lpmanem r version="1.0" enood_'.ng=="utf~8
ii? ff
,,,, drawabh <!
,t nayout
V raw ]`I>
'I'u::ker 1948 xml
{CDA'I'A[
Emenu , O Tucker foi ,;`%M2l,^1;1`g;V1j;/gl uma _`j;~qy,^`fNg
Com o objetivo de criar a lista de carros, vamos aprender a ler o arquivo XML
que colocamos no projeto, o qual contm a seguinte estrutura:
/res/raw/carros_esportivos.xmI
<?xn1 version="1.0" encodng="utf-8"?>
<url_nfo>
<url_vdeo> </ur1_vdeo>
<1atitude> </1atitude>
<1ongitude> </1ongtude>
Para ler 0 XML c criar a lista dc carros, atualize 0 cdigo da classe CarroServ\
cunibrme demonstrado a seguir.
ii CarroService.java
package br.com.livroandroid.carros.domain;
import livroandroid.lib.utils.Filetils;
import livroandroid.lib.utils.XMLUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class CarroService {
private static nal boolean LOG_0N = false;
private static nal String TAG = "CarroService";
public static List<Carro getCarros(Context context, String tipo) {
try {
String xml = readFile(context, tipo);
List<Carro carros = parserXML(context, xml);
return carros;
} catch (Exception e) {
// TODO explicar exception
Log.e(TAG, "Erro ao ler os carros: " + e.getMessage(), e);
return null;
}
carros.add(c);
}
if (LOG_ON) {
Log.d(TAG, carros.sze() + " encontrados.");
return carros;
}
I) i
'>
1 j ,`..|e 3\ff:\"*"\.i3f
'JZ' '_
Para finalizar este tpico, gostaria de ressaltar que o parser de XML pode ser feito
com SAX ou DOM. O SAX percorre o XML passo a passo, utiliza menos recursos
e por isso mais performtico. O DOM cria uma rvore em memria que repre
senta a estrutura do XML e permite acessar os elementos de forma rpida. Seo
XML for grande e voc precisar de desempenho, recomenda-se fazer o parser por
SAX, mas eu costumo utilizar o DOM pela simplicidade e nunca tive problemas. A
classe XMLUtls utilizada dentro do cdigo da classe CarroServce facilita justamente
a leitura do XML por DOM.
Tambm gostaria de ressaltar algo importante sobre o cdigo que fizemos na classe
CarroServce. Veja que o mtodo F1eUtls.readRawF1eStrng(context,raw) lana uml
exceo do tipo java.o. IOEcepton, e no mtodo CarroServce.getCarros(contet,tDl
estou fazendo o try/catch para tratar a exceo e imprimir a mensagem de erro DO
Log(at. Na prtica, isso no recomendado, pois dessa forma, se ocorrer qualquer
erro na leitura do arquivo, nenhum alerta ser dado no aplicativo e o usurio HO
ficar ciente do problema. Mas, neste momento, vamos deixar o cdigo assim.
pois o objetivo foi explicar como ler um arquivo da pasta /rcs/raw e fazer o parser
do XML. No captulo 17, sobre web services, vamos organizar melhor o cdigo C
propagar as excees adequadamente.
Captulo 16 I Parser de XML, JSON e testes unitrios 457
16.3 Parser de JSON
Outro formato de arquivo que recentemente cou bastante popular por sua sim
plicidade o JSON, o qual vem substituindo o XML como forma de integrao
de sistemas.
"carros": {
"carro": [
{
No site do livro existem estes arquivos JSON para cada tipo de carro:
http://wwwlivroandroid.com.br/livro/carros/carros_classicos.json
.,_
http://www.li1/roandroid.com.br/livro/carros/carros_esp0rti1/os.json
http://www.li1/roandroid.com.br/livro/carros/carros_luxo.json
Faa o download desses trs arquivos JSON e insira-os na pasta /res/mw do projeto
dos carros. importante que voc apague os arquivos XML que estavam na pasta
para no gerar conflitos na classe R. A figura 16.4 mostra o resultado.
ll H 1 1:zo":
" a ros": i
L , mn H Y 42) -f \ Q; ((|YDz_\lSSH.0$j$0
"t """"'
_ menu . , ,
Y' d b "nome": "Tucker -.:f48 IS ,
E lwcm
_ l ari
,at xla ..
"desc":
1no":"O"nttp:/fhttxcuek
Tucker fo; r alma
'zg' os_cIassicos
z "~ ` *~ z"',*Jr*...fO*o
tt.z://'"'.li:1
_;- u,n' n.l'..i:.,
'-' 'NH' X '
carros_2SPom\.cS_<C "u*_*7*Qe hit? - ' f' l""'`^ - J~ ~ ~
, '_saiues
_ :rrtis_lux0.j50 " l at 1 tude": " '* 2 3 . 4 22 4 " 1
3`GrdlcSripts
"lonq:Ltude":"-46.6531369
Depois de copiar os arquivos .json para a pasta /res/raw, atualize o cdigo da clas
se CarroService para fazer o parser de JSON. Basicamente vamos criar o mtodo
parserJSON(context,json) para criar a lista de carros.
CarroService.java
package br.com.livroandroid.carros.domain;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSON0bject;
public class CarroService {
private static nal boolean LOG_0N = false;
private static nal String TAG = "CarroService";
public static List getCarros(Context context, String tipo) {
try {
String json = readFile(context, tipo);
List carros = parserJSON(context, json);
return carros;
} catch (Exception e) {
Log.e(TAG, "Erro ao ler os carros: " + e.getMessage(), e);
return null;
}
carros.add(c);
}
if (LOG_ON) {
Log.d(TAG, carros.size() + " encontrados.");
}
} catch (JSONEception e) {
throw new IOEception(e.getMessage(), e);
`etU`I`l CI"I'0S
}
Pronto, isso tudo! Acredito que se voc der uma leve estudada no cdigo ver
que fazer o parser de JSON bem simples, e a classe org.json.JSONObject facilita
bastante esse trabalho. Depois dessa alterao, o aplicativo dos carros deve conti
nuar funcionando normalmente, pois mudamos apenas a classe que faz a busca
dos carros e parser das informaes. Se voc do tipo que precisa ter certeza para
acreditar, altere o nome de um carro no arquivo JSON que est no pro]eto, e esse
nome dever aparecer na lista.
Nota: se voc teve problemas para compilar o projeto, pode ser por causa de
alguma sujeira que cou nos arquivos compilados em razo de o arquivo XML ter
o mesmo nome dos arquivos JSON. Se isso aconteceu com voc, utilize o menu
Build > Clean do Android Studio para limpar os arquivos compilados a rn de V1t8f
qualqugr 0nO_ Depois faa Build > Rebuild Project e execute o projeto novamente.
450 Google Android - 40 .mo
16.4 Testes unitrios no Android
?.r~1-'i
zii famiii
fmz;-zzir-i?'.
A classe de teste unitrio deve ser lha de AndroidTestCase, que por sua vez lha
da classe TestCase do popular framework jUnit (http:/unitorg/). Essa classe per
mite executar um teste unitrio no Android facilmente, pois a nica coisa de que
precisamos o objeto android.app.Contet, o qual pode ser obtido pelo mtodo
getContet(). Com o teste em mos, clique com o boto direito no arquivo ou no
centro do editor e selecione a opo Run > (arroServiceTest. Se estiver com o cursor dentro
de um mtodo do cdigo, o Android Studio vai mostrar a opo para executar o
mtodo, como por exemplo: Run > testGetCarros().
Enm, se voc j conhece como funciona o frameworl<]Unit, nada ser novidade
para voc. Caso contrrio, recomendo procurar uma literatura adicional. Lembre
-se de que zemos o teste no nal do captulo, pois meu objetivo foi apenas de
monstrar como criar um teste unitrio no Android Studio (Figura 16.6). Lembre-se
que ease O TDD estivesse Sendo usado na prtica, esse teste seria a primeira coisa
a ser feita, mas isso assunto para outro tipo de livro.
Z Google Android-4=z
P fz-'
ll ` ie 2 H ( sit. Done
iixszxxtsq .we smz ~;;; ..;.;..;;.:;-;:;.;....;.;;s;
L.:.cu.tiv:i:azurun.\.Luxzu.s..cao
'* `l6**' rrvicr Santi. :ooowznz pm :naun -z '/eae.. ie 1
""" 0! tet()ei(.ma guctu
,5 v'btonmrvanduwdtanustHu5^flf , PNQ, d.oClup,b,_CQ_1v3n@_;
as *I tcstndmdiesti .wseetupllrcpeth ff
Web services
\4
17.1 Introduo
Uma dvida comum de quem est iniciando com mobile ou desenvolvimento de
sistemas : como fao para me conectar ao banco de dados do servidor e buscar
ou atualizar informaes nesse banco de dados?
A resposta : o aplicativo no deve conectar-se ao banco de dados do servidor.
A principal razo para isso por questes de segurana dos dados e outra por
padronizao de acesso, uma vez que web services facilitam a interoperabilidade
entre diversas plataformas, pois no importa a linguagem de programao que
o servidor ou cliente utilizam.
Para o mobile buscar informaes do servidor, necessrio utilizar um web service;
portanto, no servidor deve ser criado este Web service em qualquer plataforma ou
linguagem Uava, .NEI PI-IP, Ruby Python). A responsabilidade do Web service
acessar o banco de dados, processar as informaes e retornar os dados no formato
XML ou JSON para o mobile/cliente ler os dados.
Existem muitas maneiras de criar web services, mas isso est fora do escopo deste livro,
pois estamos falando de mobile e no de servidor. Apenas para voc complementar
seus estudos, vou citar alguns temas para voc pesquisar, caso ache necessrio.
1. WSDL (Web Services Description Language) - um formato de servio escrito em
XML, cujo trfego de dados feito via HTTP utilizando o protocolo SOAP
(Simple Object Access Protocol). Esta a maneira clssica de criar web
services. Atualmente, esse modelo est sendo evitado no mundo mobile,
463
464 Google Android - 4 edio
pois o protocolo SOAP um grande XML que trafega pela rede, e pode
causar lentido, principalmente na conexo 3G.
2. REST - Formato leve de web services que so criados geralmente sobre O
protocolo HTTP, utilizando os mtodos GET, DELETE, POST e PUT como padro_
nizao de acesso. Por exemplo, se voc zer um GET na pgina /usuario;
pode ser retornada uma lista de usurios. Se voc fizer um GET na pgina
/usuario/1, o usurio com o id=1 retornado. Mas se voc zer um DELETE na
pgina /usuario/1 , esse usurio ser deletado. Outro exemplo se zermos um
POST na pgina /usuario, que neste caso serve para inserir um novo usurio,
Ento o padro REST utiliza os prprios mtodos do`protocolo HTTP para
criar a lgica necessria para o web service. No REST 0 formato de retorno
mais comum o JSON.
3. Pginas simples com GET ou POST - Outra forma de criar um web service em
qualquer linguagem fazer uma pgina web qualquer que, ao receber uma
requisio por GET ou POST vai retornar os dados no formato XML ou
JSON, em vez de retornar uma pgina HTML.
Essa rpida explicao visa informar os termos necessrios para voc fazer uma
pesquisa no Google e complementar seus estudos, caso ache necessrio.
Neste livro, vamos utilizar arquivos XML e JSON estticos que coloquei no site,
apenas para simular um web service. Conforme j vimos, existem trs arquivos
para cada tipo de carro e podemos optar por ler os dados em XML ou JSON.
// XML e JSON dos carros clssicos
http://www.1ivroandroid.con.br/livro/carros/carros_c1assicos.xml
http://www.1ivroandroid.com.br/livro/carros/carros_c1assicos.json
CarroService.java
import livroandroid.lib.utils.HttpHelper;
public class CarroService {
private static nal String
URL = "httpz//www.livroandroid.com.br/livro/carros/carros_{tipo}.json";
public static List getCarros(Context context, String tipo) throws IOException {
String url = URL.replace("{tipo}", tipo);
// Faz a requisio HTTP no servidor e retorna a string com o contedo.
String json = HttpHelper.doGet(url);
List carros = parserJSON(contet, json);
return carros;
}
Depois de alterar a classe CarroServce para fazer a requisio HTTP no web service,
execute o projeto novamente. O resultado que a aplicao vai travar, mostran
do o famoso Force (lose para o usurio. Se olharmos nos logs, veremos a seguinte
mensagem de erro (stack trace):
br.com.1ivroandroid.carros E/AndrodRuntme? FATAL EXCEPTION: main
Process: br.com.1vroandroid.carros, PID: 988
android.os.Network0nManThreadException
at android.os.StrctMode.onNetwork(StrctMode.java:1147)
Conforme j estudamos anteriormente no captulo 10, sobre threads e Handler, esse erro
acontece porque no podemos acessar a internet na UI Thread, portanto precisamos
criar uma thread ou AsyncTask para desvincular o processamento da th read principal, c.
se possvel, ainda mostrar uma animao no estilo por favor, aguarde para o usurio,
pois a consulta pode demorar, no caso de um dispositivo utilizando 3G.
}
// Atualiza a interface
protected void onPostExecute(List<Carro carros) {
if(carros != null) {
CarrosFragment.this.carros = carros;
// Atualiza a view na UI Thread
recyclerview.setAdapter(new CarroAdapter(getContext(), carros, onClickCarro()));
}
Nesse cdigo criamos uma AsyncTask para consultar o web service em segundo
plano (background) e atualizar a view na UI Thread. Esta linha dispara a thread:
new GetCarrosTask().eecute();
AndroidManifest.xmI
<manfest . . . />
uses-permission android:nane="androd.permisson.INTERNET" /
<app1icaton ... />
</manfest>
Ao executar o projeto novamente, a consulta no web service ser feita e voc dever
ver a lista de carros conforme a gura 111. Isso muito legal, pois desta vez a lista
de carros est online, e essa conectividade 0 que d vida palavra mobilidade`
A_ -__)~] ___ `
1-1.
l iH;,lz
Figura 121 - Lista de carros do web service.
NI'1...
17.4 Biblioteca simples para encapsular a AsyncTask
o topico anterior, buscamos os carros do web service utilizando a famosa classe
AsyncTask, mas eu particularmente costumo utilizar uma pequena biblioteca que
encapsula o acesso AsyncTask.
Captulo 17 I Web services 469
Ao utilizar a AsyncTask, temos de controlar vrias coisas manualmente, como por
exemplo: mostrar um ProgressBar ou outra animao durante a execuo da tare
fa, tratar as possveis excees que so lanadas, ter uma maneira de criar vrias
tarefas e cancel-las, alm de tratar vrios probleminhas comuns com o Android
quando o dispositivo rotacionado entre vertical e horizontal.
Na verdade, eu no quero entrar nesses assuntos avanados da AsyncTask neste
momento do livro, pois acho cedo para termos essa conversa. O objetivo do livro
ir aumentando o grau de diculdade durante a leitura, ao mesmo tempo em
que vamos conectando diversos conceitos, e justamente por isso estamos fazendo
esse projeto dos carros passo a passo.
Portanto, por ora, vamos utilizar esta pequena biblioteca que criei para disparar
tarefas em segundo plano, e depois recomendo que voc leia o captulo 31, sobre
AsyncTask, disponvel no nal do livro. Com o tempo, voc pode estudar o cdigo
-fonte da biblioteca que vou mostrar e voc vai perceber que apenas encapsulei
coisas que voc um dia tambm ter de fazer.
Bem, chega de teoria, ento vamos l!
A classe livroandroid.lib.fragment.BaseFragment da biblioteca android-utils contm o
mtodo startTask(codigo,taskListener), que basicamente vai disparar uma AsyncTask e
controlar a sua execuo. Como parmetro do mtodo startTask(codigo,taskListener),
voc deve informar uma implementao da interface TaskListener.
public interface TaskListener {
// Executa em background numa Thread e retorna o objeto
T execute() throws Exception;
// Atualiza a view na UI Thread
void updateView(T response);
// Chamado caso o mtodo execute() lance uma exceo
void onError(Exception exception);
// Chamado caso a task tenha sido cancelada
void onCancelled(String cod);
}
- ' e o T vire um
Nota- 3 notao um tipo genrico e permite que esta interface seja criada
com qualquer tipo, por exemplo, com TaskListener, para qu
Objeto Carro em tempo de execuo. Se achar necessrio, recomendo procurar uma
hteratura adicional ggbre Generics, pois o assunto bem amplo e interessante.
470 Google Android - 4 edio
Acredito que os mtodos da interface Tasktistener sejam autoexplicativos e at
se parecem com os mtodos doInBackground() e onPostExecute() da classe AsyncTask.
Portanto, vamos atualizar o cdigo da classe CarrosFragnent para utilizar o mtodo
startTask(codigo,taskListener), e durante o desenvolvimento do aplicativo vou re
forando algumas das vantagens dessa biblioteca. O cdigo anterior que usava 3
AsyncTask diretamente voc pode remover.
LT CarrosFragment.java
@0verride
public void updateView(List<Carro carros) {
if (carros != null) {
// Salva a lista de carros no atributo da classe
CarrosFragnent.this.carros = carros;
// Atualiza a view na UI Thread
recyclerview.setAdapter(new CarroAdapter(getContext(), carros, onClickCarro()));
}
@0verride
public void onError(Exception e) {
// Qualquer exceo lanada no mtodo execute vai cair aqui.
alert("0correu algum erro ao buscar os dados.");
}
@0verride
}
Captulo 17 1 Web services 471
Ao executar o projeto novamente a lista de carros ser mostrada da mesma for
ma que antes. Mas veja que durante o download foi exibido um ProgressDa1og
conforme a figura 112.
Bugatti Veyron
3
Frrari Enzo
Lamborghinr Reventon
; , Y
Lebianc Mrabau
/res/Iayout/fragment_carros.xmI
<?xm1 version="1.G" encodng="utf-8"?>
<FrameLayout . . .>
<androd.support.v7.wdget.Recyc1erView . . . />
<ProgressBar android:d="@+d/progress"
and rod : 1ayout_width="wrap_content" android :1ayout_heght="wrap_content"
androd:1ayout_gravty="center" androd:vsb1ty="nvsib1e"/>
O resultado dessa alterao pode ser visto na gura 173, que mostra a animao
.lo ProgressBar durante a execucao da ta refa. Lembrando que esse tipo de animao
muito importante para melhorar a experincia do usurio, principalmente no
cascidezuwhcatuuiseracessadcun uniaxniex3(]intutolerna.
ta , V v
.za
Uma dica, ou melhor, um artifcio tecnico, caso queira ver as animaes, e dar um
pequeno sIeep" dentro do metodo eecute(). Algo como 300 u 500 milissegundos
ja e su hciente para voc ver a aniinaco.
public List execute() throws Exception {
Thread.sleep(500);
return CarroService.getCarros(getContet(), tipo);
}
(.omo vimos, utilizar uma pequena classe que encapsula a AsyncTask tein algumas
Vfllllilll, pois ao utilizar a classe AsyncTask voc tera de mostrar e esconder u
ProgressBar inanualinente.
Captulo 17 z Web services 473
Dica: a animao do ProgressBar suave, recomendo utiliz-la para informar ao
usurio que a aplicao est processando algo. j o ProgressDialog deve ser utilizado
caso o usurio precise obrigatoriamente aguardar o processamento, pois a janela
do alerta no estilo modal. Lembrando que, no caso dos tablets, como podemos
ter um layout com vrias views espalhadas pela tela, o recomendado que cada
fragment utilize um ProgressBar, pois as animaes de cada um sero independentes.
Neste caso, teremos vrias bolinhas girando espalhadas pela tela do tablet.
/res/layout/fragment_carros.xml
<?xnl version="1.0" encoding="utf-8"?>
<FrameLayout . . .>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeToRefresh"
android:layout_width="natch_parent" android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView . . . />
<ProgressBar . . . />
CarrosFragment.java
public class CarrosFragment extends BaseFragment {
private SwipeRefreshLayout swipeLayout;
// Swipe to Refresh
swipeLayout = (SwipeRefreshLayout) view.ndViewById(R.id.swipeToRefresh);
swipeLayout . set0nRefreshListener(0nRefreshListener( ) );
swipeLayout . setColorScheneResources(
474 Google Android - 4 edio
R.color.refresh_progress_1,
R.color.refresh_progress_2,
R.color.refresh_progress_3);
return view;
};
}
Chevrolai
L/ Corvetve
Para controlar quando utilizar uma animao ou outra, vamos criar um parme
tro do tipo booleano para o mtodo taskCarros(boolean), a m de que seja possvel
escolher entre animar o ProgressBar ou o SwipeRefreshLayout.
CarrosFragment.java
public class CarrosFragnent extends BaseFragment {
taskCarros(true);
i
};
}
@0verride
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
taskCarros(false);
}
si AndroidUtiIs.java
public class AndroidUtils {
if (connectivity == null) {
return false;
Captulo 17 u Web services 477
} else {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == Networklnfo.State.CONNECTED) {
return true;
}
} catch (SecurityException e) {
alertDialog(context,e.getClass().getSimpleName(), e.getMessage());
}
return false;
}
AndroidManifest.xmI
<nanifest . . . />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETw0RK_STATE" /
<application ... />
CarrosFragment.java
public class CarrosFragment extends BaseFragment {
};
}
/res/values/strings.xmI
<'eSOU 'CeS>
\r$
.tw .A_yw
~._./
e seu wi F!
Observe que a lgica aplicada aqui verifica se existe conexo apenas no momento
de fazer O gesto de Pull to Refresh, porm no veriquei se existe conexao antes de
Chamar 21 mta da Pflmlf Vez quando a lista est vazia. Na verdade, nao z issu
POYQU O PYXW C3PlUl0 vamos salvar os carros no banco de dados, assim O
Captulo 17 n Web services 479
aplicativo poder trabalhar de forma offline. Somente para a atualizao da lista
que vamos precisar fazer alguma validao.
Outro detalhe importante que no captulo anterior comentei algo sobre o trata
mento das excees, e na poca z um try/catch dentro da classe CarroService, o que
matou a exceo, e isso no recomendado. Mas, se voc olhar o cdigo atual da
classe CarroService, ele no tem mais esse problema, e se alguma exceo do tipo
IOEception ou RuntimeException for lanada, ela ser propagada, ou seja, ser lanada
para cima. Isso signica que, caso o mtodo execute() lance uma exceo, a biblio
teca android-utils vai tratar o erro internamente fazendo um try/catch e vai chamar
o mtodo onError(eception) para voc fazer o tratamento adequado da exceo.
Enfim, j vimos vrias vantagens de utilizar esta pequena biblioteca para disparar
tarefas assncronas.
private class GetCarrosTask implements TaskListener<List> {
public List execute() throws Exception {
// Se este mtodo lanar uma exception...
}
public void updateView(List carros) { }
public void onError(Eception e) {
// Vai cair aqui para voc tratar o erro.
alert("0correu algum erro ao buscar os dado: " + e.getMessage());
}
Apenas para voc traar um paralelo, sempre que voc digita uma URL no browser
para abrir o site desejado, feita uma requisio do tipo GET. A diferena que o
480 Google Android - 4 e<|
site retorna os dados no formato HTML, j o web service geralmente retorna 05
dados em XML ou JSON.
Por sua vez, uma requisio do tipo POST envia os parmetros no corpo da requ_
sio HTTP, e a URL apenas contm o endereo Final (endpoint) em que vo
deseja enviar/postar determinado contedo.
http://www.site.com.br
No corpo da requisio, so enviados os parmetros, por exemplo;
{paramt=va1or1¶m2=va1or2}. A vantagem da requisio do tipo POST que, caso vo
precise enviar informaes secretas como uma senha, ela enviada no corpo da
requisio. Se fosse utilizado o GET, a senha seria enviada na URL e caria visvel
junto com os demais parmetros.
Traando um paralelo mais uma vez, quando voc faz login no Gmail ou em
qualquer formulrio de cadastro na internet, geralmente feita uma requisio
do tipo POST no site. A requisio do tipo GET tambm tem um limite em relao
quantidade de parmetros que podem ser enviados, portanto o POST tambm
recomendado para trafegar muitas informaes.
Outro exemplo em que o POST deve ser utilizado para trafegar arquivos, tais como
fazer o upload de fotos. Estou explicando isso porque o web service dos carros
foi utilizado apenas para consulta, e isso um clssico exemplo de utilizao do
GET. Mas se fosse necessrio salvar um carro no servidor, provavelmente os dados
seriam enviados por POST.
Eu no tenho um web service desse tipo disponvel, ento, para brincarmos um
pouco com requisies HTTP do tipo POST, vamos utilizar este web service dis
ponibilizado no site da W3SchooIs:
l'_ _
/,_ ._~.m\.,.. ._ zxz -.~.. .~....; z.....z._.......z=.zz..:,
L
;___ _ . *
1 _ g _ wyy~i.w3scl~iools.oon1/websewices/te~mpconvereasmx sf;
i ` The following operations are supported. Por a Formal deniton, please review the Service Deserigtion.
* .i!'..'I.'9.{w.e_.z.,l.!_,._ HMP" f
z ~ tTog. lsigis
l' ' .
l
.~ z __ E X
i fl TempConvert Web S _
Figura 127 - Web service no site da W3Schools.
*
l
|_
l TempConvert
H Click here for a complete list of operations.
,_
l
_
l l CeIsiusToFahrenheit
il i
Test
1l *Inloke
l"
Celsius: il5`
Pararnetarvalue i ` 7 V ` _ _ ' ' z `~ 1
E l; T0 test the .:eret. usina the .H1TP_ PQST Prewelz lik fh.._'If1vlT buttm
1 SP 1.1
l
zi
l ll The following is a sample SOAP 1.1 request and response. The placeholders shown need to be replaced with actual values.
l
, lHuse: ru-.1v3:c1xoo1s.:nm ~ , *
i
5i flzction:
Ccn1ten1:'kng-vh:
Cunteut-Type: text/xml; :hzz:e1=1:1:f-B
length _
fz _ 1
; `^ :cizp:Hmi> _
'keep : ,fwnru . rschonls . :onJ'\eb:~ez'v5.:esCelsu:'!`nf=.h:eabe;u'
?n|.\1 verei:-n="1.D' :m:ndu.q=utf"2>- . \
z
-z TgnpQzj1&f1I`i~gb xe @www.w3schools.com_ x _ AA __
ll a C' [] www.w3schools.com/Websewices/tempconvertasmx/CelSfffg
This file dggg not appear to have any style fflti associated With it. The
l
_ ...___ : z --~
Figura 129 - Resultado do web service.
482 Google Android - 4= edo
O retorno do web service um pequeno XMLque contm a tag <strngva1or</5mng>_
Esse retorno ser diferente em cada web service, mas o importante nesse caso
que sabemos que podemos fazer o POST nesta URL passando o parmetro Celsius.
http://www. w3schools. com/webservices/tempconvert. asmx/Celsius ToFahrenheit
A partir daqui, os exemplos no sero feitos no projeto dos carros, pois esse as
sunto complementar aos seus estudos. Para continuar, crie um projeto chamado
HeU.owSDL ou abra o projeto de exemplo pronto que est no GitHub.
Tenha ateno, pois como vamos acessar a internet necessrio declarar a per
misso INTERNET. Todos os prximos exemplos vo utilizar o mesmo arquivo dg
layout, que um simples formulrio que permite digitar o valor em Celsius para
converter para Fahrenheit.
/res/Iayout/activity_form_ceIsius_to_fahrenheit.xmI
<LnearLayout n1ns:androd="http://schemas.android.com/apk/res/android"
android:1ayout_width="natch_parent" android:1ayout_height="natch_parent"
android:paddng="16dp" android:orientation="vertica1">
<TetView
android:1ayout_width="match_parent" android:1ayout_height="wrap_content
android:text="Ce1cius"/>
<EdtText androd:d="@+id/tCe1cius"
android:1ayout_wdth="match_parent" androd:1ayout_height="wrap_content" />
<TetView
androd:1ayout_width="match_parent" android:1ayout_heght="wrap_content"
android:text="Fahrenheit"/>
EditText androd:id="@+id/tFahrenheit"
android:1ayout_wdth="match_parent" androd:1ayout_height="wrap_content"
android:edtab1e="fa1se" android:inputType="number"/>
Button
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content"
android:tet="Converter" android:1ayout_gravity="right"
android:onC1ck="onC1ickConverter" />
CelsiusToFahrenheitPostActivity.java
});
} catch (IOEception e) {
Log.e("livroandroid", "Erroz " + e.getMessa9@(), G);
}
}.start();
}
Observe que estou utilizando as classes HttpHe1per e XMLUtils para fazer o POST no
web service e para ler o retorno do XML, portanto declare a dependencla da
biblioteca android-utils.
484 Google Android - 41 d
app/build.gradle
dependencies { . . .
conpile 'br.con.1ivroandroid:android-utiIs:1.6.0'
}
p Celcuis.
1 ll
i
nftfc-^
33.8
CONVERTER
No aplicativo dos carros, estamos utilizando os arquivos XML e JSON que cSI0
no site para simular um web service, pois esses dois tipos de retornos so leveS C
essa arquitetura bem simples e muito utilizada.
Mas, dependendo do servio que existe no servidor, s vezes necessrio consumir
web services clssicos, que so descritos por uma WSDL (Web Service Deniti
Language). Neste caso, uma mensagem SOAP (Simple Object Access protocol)
enviada para trafegar os dados. Enm, este no um livro de web services, ent0
se for necessrio, procure alguma literatura adicional.
O fato que s vezes precisamos acessar esses tipos de servios, e para isso Pff'
cisamos gerar um cliente de web service, a m de chamar os mtodos rem0f05
que foram definidos no servidor. No Android, podemos utilizar a biblioteca K53
para acessar esses web services. Essa biblioteca pequena e leve, e basicamlc
encapsula o trabalho chato de criar as mensagens SOAP.
Captulo 17 I Web services 485
http://leobjects.org/ksoap2/
https://code. google. com/p/ksoap2-android/
app/buiId.gradIe
dependencies {
compile 'br.com.livroandroid:android-utils:1.0.0'
// Biblioteca KSOAP2 para web services WSDL
compile 'com.google.code.ksoap2-android:ksoap2-android:3.1.1'
}
Para testar o Web service, vamos utilizar o mesmo servio disponibilizado no site
da W3Schools.
http://www.w3schools.com/webservices/tempcom/ert.asmx
Nesta pgina, se voc clicar no link Service Description, a pgina vai redirecionar para
o seguinte link, que apenas adiciona o parmetro ?wSDL no nal da URL:
http://www.w3schools.com/webservices/tempconvert.asmx?WSDL
Ao fazer isso, voc ver o arquivo WSDL desse servio, que basicamente declara
todos os mtodos que o Web service contm. O que geralmente fazemos gerar
um cliente de Web service, utilizando ferramentas que geram cdigo a partir desse
arquivo WSDL. Mas a biblioteca KSoap oferece uma alternativa para esta gerao
de cdigo e permite voc chamar os mtodos desse Web service manualmente.
Para chamar um mtodo de um web service, precisamos montar um XML no
formato do protocolo SOAP e fazer post na pgina do web service. Como isso
trabalhoso, o KSoap permite que voc apenas congure o nome do mtodo, os
parmetros que deseja enviar e internamente ele faz o trabalho pesado para criar
a mensagem no formato SOAP
A seguir, podemos verificar um exemplo de cdigo que mostra como executar o
mtodo CelsiusToFahrenheit deste web service.
CeIsiusToFahrenheitKSoapActivity.java
public class CelsiusToFahrenheitKSoapActivity extends AppCompatActivity {
});
}
} catch (Exception e) {
throw e;
}
return "";
}
Nota: vale lembrar que web services com WSDL esto sendo cada vez menos
utilizados como servios, principalmente no mundo mobile. Um dos motivos
disso porque o protocolo SOAP pesado, pois na prtica ele um grande
arquivo XML que ca indo para l e para c, trafegando na rede. Portanto,
preferencialmente, se puder escolher, crie servios simples que retornam os
dados em JSON.
Nessa pgina, basta voc digitar a URL da WSDL do Web service e clicar para gerar
o cdigo. necessrio fazer um cadastro antes, mas at o momento que este livro
estava sendo escrito esse servio era gratuito. Depois de gerar o cdigo cliente do
servio, copie as classes para o projeto do Android Studio. No caso deste servio da
W3SchooIs, a classe que foi gerada chama-se TenpConvert e basicamente encapsula a API
do KSoap2, facilitando a vida do desenvolvedor para utilizar o web service. Interna
mente, essa classe vai utilizar exatamente a mesma API do KSoap2 que j estudamos.
A seguir, temos um exemplo de cdigo que utiliza a classe TenpConvert que foi ge
rada automaticamente pela pgina WW1zlWSdl2C0d6.CO77'l. Veja como o cdigo nal
simples. Podemos concluir que, se for necessrio acessar web services criados
com WSDL, vale a pena utilizar algum gerador de cdigo como zemos aqui.
488 Google Android - 41 Qdio
ffl CeIsiusToFahrenheitActivty.java
});
}
}.start();
}
Neste captulo, aprendemos a consultar web services e ler o retorno nos formatos
XML e JSON, alm de termos estudado como consultar servios com WSDL.
Lembre-se de que, sempre que acessar a internet, preciso abrir uma thread para
no travar o processamento, e para isso utilizamos o mtodo startTask(codig0.
tasktistener, progress) da biblioteca android-utils. Eu particularmente fao assim
em meus projetos, e inclusive utilizo uma verso melhorada e com mais mtod05
da mesma classe HttpHelper que mostrei aqui.
Existem muitas outras bibliotecas para consultar web services no Android, C
devido variedade cada desenvolvedor acaba optando por utilizar a biblioteCH
que mais o agrada.
Apenas para fechar o captulo, vale a pena explicar um clssico bug referente 21
requisies HTTP no Android. Desde o incio no Android, existem duas clas55
que podem fazer requisies HTTP: a classe HttpURLConnection e a classe Httptliet
da famosa biblioteca da Apache. Mas, segundo um post ocial no blog do Google
Developers, a biblioteca HttpURLConnection apresenta alguns bugs at a verso 2.2 dv
\
Captulo 17 I Web services 439
Android, e deve-se utilizar a biblioteca HttpCl.ent. No entanto, do Android 23 ou su~
perior, esse bug foi resolvido, e o Google recomenda utilizar a classe HttpURLConnecton.
Para evitar esta salada de fruta, podemos utilizar uma biblioteca que encapsula esses
problemas e faz a correta utilizao destas APIs dependendo da verso do Android.
Esta biblioteca a 0KHttp, que est disponvel no seguinte endereo:
http://squaregithub.io/okhttp/
Sua utilizao bem simples, e recomendo que voc d uma olhada. Eu no posso
demonstrar exemplos de cada uma das bibliotecas neste livro, mas recomendo
uma leitura complementar para complementar seus estudos.
Android Developers Blog - Explicao do problema clssico com
I-IttpURLConnection vs I-IttpClient.
http://android-developers.blogspotcom.br/2011/09/androids-http-clients.html
0kHttp - Biblioteca para fazer requisies HTTP no Android.
http://squamgithub. io/okhttp/
Retrot - Biblioteca muito famosa para acessar servios REST
http://squaragithub. io/retrot/
Volley - Biblioteca ocial do Google para fazer requisies HTTP de forma
assncrona. Recomendo assistir a palestra feita no Google I/ O 2013 sobre
o Volley
http://developer android.com/training/1/olley
Persistncia
"*'1
Prefs.java
package 1vroandrod.1ib.ut1s;
import androd.content.Contet;
490
Captulo 18 I Persistncia
import android.content.SharedPreferences;
public class Prefs {
// Identicador do banco de dados destas preferncias
public static nal String PREF_ID = "livroandroid";
public static void setBoolean(Context context, String chave, boolean on) {
SharedPreferences pref = context.getSharedPreferences(PREF_ID, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(chave, on);
editor.commit();
}
tfsi (arrosTabFragment.java
inport livroandroid.lib.utils.Prefs;
public class CarrosTabFragnent extends BaseFragnent {
private TabLayout tabLayout;
private ViewPager viewPager;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
@0verride
public void onTabSelected(TabLayout.Tab tab) {
// Se alterar a tab, atualiza o ViewPager
nViewPager.setCurrentIten(tab.getPosition());
// Salva o indice da pgina/tab selecionada
Prefs.setInteger(getContext(), "tabldx", nViewPager.getCurrentIten());
}
Feitas essas alteraes, execute o projeto novamente e selecione alguma tab. Depois
feche o aplicativo com o boto voltar e inicie o aplicativo novamente. O resultado
e que a aplicao ser iniciada com a ltima tab/pgina selecionada.
Neste exemplo, utilizamos a classe Prefs, que eneapsula o acesso classe
SharedPreferences. Conforme acabamos de ver, salvar pequenas informaes no
formato de chave e valor muito til em casos simples como esse, e a vantagem
que internamente o Android cria um banco de dados interno da a plicao para
armazenar essas informaoes.
Captulo 18 n Persistncia 493
18.2 Activity de conguraes
comum encontrar nos aplicativos telas de configuraes (settings) para o usu
rio personalizar os dados. Sendo assim, para praticarmos, vamos implementar
uma dessas telas no projeto dos carros, por isso deixamos preparado o menu
(onguraes no menu lateral.
. . |
lista de carros e a pgina com o WebView. Mas, ao selecionar o item Conguraes no
menu, vamos abrir uma nova activity devido a problemas tcnicos.
V Notificacoes I
5562HVGA_
ConguracoesActivvity.java
package br.com.livroandroid.carros.activity.prefs;
@SuppressHarnings("deprecation")
public class ConguracoesActivivity extends android.preference.PreferenceActivity {
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Carrega as conguraes
addPreferencesFromResource(R.ml.preferences);
}
A prxima activity chamei de V11, para lembrar que ela compatvel com Android
3.0 (API Level 11) ou superior. Note que ela declara um fragment interno do tipo
android.preference.PreferenceFragnent e o insere como a raiz do layout.
Conguracoesvl1Activivity.java
package br.com.livroandroid.carros.activity.prefs;
@TrgetApt(But1d.vERs1oN_cooEs.HoNEvcoMB)
public class ConguracoesV11Activivity extends android.app.Activity {
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
// Adiciona o fragment de conguraes
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(android.R.id.content, new PrefsFragment());
ft.comnit();
}
C3PtuIo 18 I Persistncia 495
public static class PrefsFragment extends android.preference.PreferenceFragnent {
@Override
/res/xml/preferences.xmI
<?ml version="1.0" encoding="utf-8"?>
<PreferenceScreen mlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Livro Android">
<CheckBoPreference
android:key="PREF_CHECK_PUSH"
android:summary="Receber alertas de Push sobre lanamentos de novos carros."
android:title="Noticacoes" />
Esse arquivo XML declara uma tela de preferncias PreferenceScreen e cria uma
categoria PreferenceCategory chamada Livro Android, sendo que uma categoria pode
conter vrios componentes. O item CheckBoPreference mostra um checkbox para
o usurio selecionar uma opo. A vantagem de utilizar esse arquivo XML que
todos os valores so salvos automaticamente pelo Android, sem a necessidade de
nenhuma programao. Observe que no arquivo XML foi denido que a chave
do CheckBoxPreference PREF_CHECK_PUSH, conforme este cdigo:
Isso significa que, depois de salvar os dados na tela de configurao, basta lermos o
valor da chave PREF_CHECK_PUSH com a classe SharedPreferences.A classe PrefsUtils mostra
como ler se o checkbox est selecionado. Note que criei o pacote util no projeto
para adicionar as classes utilitrias que vamos criando durante o desenvolvimento.
PrefsUti|s.java
package br.com.livroandroid.carros.util;
public class PrefsUtils {
495 Google Android - 4 edio
// Verica se o usurio marcou o checkbox de Push ON nas congura@S
public static boolean isCheckPush0n(nal Context context) {
SharedPreferences sp = Preferenceanager.getDefaultSharedPreferences(context);
return sp.getBoolean("PREF_CHECK_PUSH", false);
}
Para a activity compatvel com Android 3.0 ou superior, vamos customizar as cores
do tema Material, mas no podemos utilizar o tema AppCompat, pois eSSa Uma
activity nativa que herda de android.app.Activity. Sendo assim, vamos criar mais um
tema chamado AppThene.Material. Repare que, embora a activity seja compatvel com
Android 3.0 (API Level 11), vamos customizar as cores apenas para o Android 5.0
(API Leve 21), pois foi quando o tema Material foi criado. Para verses anteriores,
a tela de configuraes ter a interface padro de cada plataforma.
/res/values/styles-v21.xml
<iten nane="android:colorPrinary">@color/primary/iten>
<iten nane="android:colorPrinaryDark">@color/prinary_dark/iten
item nane="android:colorAccent">@color/accent/iten>
AndroidManifest.mI
<activity android:name=".activity.prefs.ConguracoesActivivity"
android:label="Conguraes">
<meta-data android:nane="android.support.PARENT_ACTIVITY"
android:value=".activity.MainActivity" />
<activity android:name=".activity.prefs.ConguracoesV11Activivity"
android:label="Conguraes"
Captulo 18 u Persistncia 497
android:parentActivityName=".activity.MainActivity"
a"d"1d ="e="@sty1e/AppThene . Material">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android : value=" .activity . MainActivity" />
Uma vez que as activities esto devidamente conguradas, altere o mtodo que
trata o evento de clique do menu lateral para chamar a activity apropriada con
forme a verso do Android.
MainActivity.java
@0verride
public void onNavDrawerItemSelected(. . .) {
if (position == 0) { . . . }
else if (position == 1) { . . .}
else if (position == 2) { // Abrir as conguraes...
if (Androidtils.isAndroid3Honeyconb()) {
startActivity(new Intent(this, ConguracoesV11Activivity.class));
} else {
startActivity(new Intent(this, ConguracoesActivivity.class)) ;
}
O resultado deve ser como a gura 18.1, que foi apresentada no incio deste tpico.
Lembre-se de que, depois de salvar os dados na tela de configuraes, voc precisa
ler o valor que foi salvo, e para isso criamos a classe PrefsUtils.
boolean b = PrefsUtils.isCheckPushOn(this); // true se marcou
Onde inserir esse cdigo para ler essa informao deixarei como lio de casa para
voc pois o objetivo foi apenas mostrar como criar esse tipo de tela de configura
7
(arroService.java
` )CS
ou voce deve estar na pasta /arzdrozd-sd/z/pIairm-iIs/ na qual o adb Lbf mf
No Android Studio, para visualizar o local onde o SDK esta instalado, util `
menu File > Project Structure > SDK Location. Aproveite c vcriquc as outris opzl
dessa janela, pois h mais informaes interessantes nela.
(>
A Figura 18.2 mostra os comandos explicados e os arquivos salvos na memoria
interna do emulador.
DIRECTORY_DONNLOADS, Ou DIRECTORY_DCIM.
502 Google Android - 4 edio
// Retorna a pasta do SD card (/sdcard/DCIM)
File sdcardDir = Environment.getEternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
AndroidManifest.mI
<nanifest . . . />
<uses-permission android:nane="android.pernission.INTERNET" />
<uses-permission android:nane="android.pernission.ACCESS_NETw0RK_STATE" />
<uses-permission android:nane="android.permission.HRITE_EXTERNAL_STORAGE" />
<uses-permission android:nane="android.permission.READ_EXTERNAL_STORAGE" /
<application ... />
(arroServce.java
Caso queira brincar com esses mtodos, insira o seguinte cdigo dentro de qual
quer activity:
Log.d tag","getF1esDr > " + getFi1esDir());
Log.d tag","getFlesDir > " + getFi1eStreamPath("arquivo.tt"));
Log.d tag","getF1esDir > " + getExterna1F1esDr(Environment.DIRECTORY_DCIM));
Log.d tag","getF1esDir > " + getCacheDir());
O projeto dos carros est buscando os carros do servidor, fazendo uma requisi
o HTTP no web service que est no site. Mas j que aprendemos a salvar os
arquivos, por que no aproveitamos e fazemos um cache?
Caso o JSON dos carros esteja salvo em arquivo, podemos ler o JSON direta
mente desse arquivo e evitar uma conexo com a internet, alm de melhorar o
desempenho do aplicativo. A lgica para a busca ser a seguinte:
1. O aplicativo deve fazer a consulta dos carros tentando ler o arquivo JSON
que est salvo na memria interna.
2. Se o arquivo existir, faz a leitura e retorna a lista de carros.
CarroService.java
l
private static List parserJSON(Contet context, String json) throws I0EceDU0 U
}
Veja que no codigo estou salvando os arquivos na memria inteml, 6 11510 alterei
nada nessa parte de salvar arquivos, nem no mtodo que faz o parser do JSON.
Portanto, se voc executar 0 projeto novamente, tudo dever continuar funcio
nando. Caso os arquivos no existam na memria interna, voc ver as seguintes
mensagens no I.ogCat:
Abrindo arquivo: carros_1uo.json
Arquivo (carros_1uxo.json) no encontrado.
URL: http://www.iivroandroid.con.br/livro/carros/carros_1uo.json
Arquivo salvo: /data/data/br.com.1ivroandroid.carros/ies/carros_1uxo.json
E. caso os arquivos ja existam, sera leito o cache e voc ver as seguintes mensagens:
Abrindo arquivo: carros_iuo.json
Carros lidos do arquivo carros_1uo.json
Nesta brincadeira, voc aprendeu a salvar e ler arquivos, tanto na memoria interna
quanto na memria externa. O resultado at que cou legal, pois o aplicativo dos
carros fez cache dos arquivos, portanto, a lista sera carregada mais rapidamente.
Mas o nosso principal objetivo neste captulo salvar os carros em um banco de
dados, c no em arquivos. Portanto, no prximo topico vamos comear o nosso
estudo sobre SQLite.
P)
Utilizando o aplicativo sqIite3 pelo console do emulador. Mais detalhes sobre
o sqIite3 podem ser acessados em http://www.sqlite.org/sqlite.html.
Basicamente, podemos dizer que existem duas maneiras de criar o banco de
dados no aplicativo. Ou utilizamos a API e escrevemos os scripts SQL para criar
as tabelas, ou criamos o banco de dados com uma ferramenta externa e depois
importamos o banco de dados j pronto no projeto. Neste ltimo, voc precisa
escrever algum cdigo que vai copiar o arquivo do banco de dados j pronto para
dentro das pastas do aplicativo.
No ro`eto dos carros o banco de dados ser criado utilizando a rimeira o o
que utilizando a API e executando um script SQL para criar as tabelas neces
srias para o aplicativo funcionar.
Eu acredito que a API do Android seja to simples que a maneira mais fcil de
explicar mostrar logo de uma vez o cdigo-fonte da classe que vamos utilizar.
Ento crie a classe CarroDB no pacote domain, conforme demonstrado a seguir. Essa
classe contm mtodos para listar todos os carros por tipo, salvar ou atualizar
um carro, excluir um carro e excluir todos os carros pelo tipo.
CarroDB.java
package br.com.livroandroid.carros.domain;
public class CarroDB extends SQLite0penHelP@f f
private static final String TAG = "SCi1"
// Nome do banco
Google Android - 4= edio
@0verride
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "Criando a Tabela carro...");
db.execSQL("create table if not exists carro (_id integer primary key
autoincrenent,nome text, desc text, url_foto text,url_info text,url_video text,
latitude text,longitude text, tipo tet);");
Log.d(TAG, "Tabela carro criada com sucesso.");
}
@0verride
public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
// Caso mude a verso do banco de dados, podemos executar um SQL aqui
}
// Insere um novo carro, ou atualiza se j existe
public long save(Carro carro) {
long id = carro.id;
SQLiteDatabase db = getwritableDatabase();
try {
Contentvalues values = new ContentValues();
values.put none", carro.nome);
values.put("desc", carro.desc);
values.put("url_foto", carro.urlFoto);
values.put| url_info", carro.urlInfo);
values.put url_video", carro.urlVideo);
values.put latitude", carro.latitude);
values.put longitude", carro.longitude);
values.put tipo", carro.tipo);
if (id != 0) {
String _id = String.valueOf(carro.id);
String[] whereArgs = new String[]{_id}
// update carro set values = ... where _id=?
int count = db.update("carro", values, "_id=?", whereArgs);
return count;
} else {
// insert into carro values (...)
id = db.insert("carro", "", values);
~return id;
}
} nally {
509
Captulo 18 n Persistncia
db.close();
}
}
// Deleta o carro
public int delete(Carro carro) {
SQLiteDatabase db = getwritableDatabase();
try {
// delete from carro where _id=?
int count = db.delete("carro", "_id=?", new String[]{String.value0f(carro.id)});
Log.i(TAG, "Deletou [" + count + "] registro.");
return count;
} nally {
db.close();
}
}
// Deleta os carros do tipo fornecido
public int deleteCarrosByTipo(String tipo) {
SQLiteDatabase db = getwritableDatabase();
try {
// delete from carro where _id=?
int count = db.delete("carro", "tipo=?", new String[]{tipo});
Log.i(TAG, "Deletou [" + count + "] registros");
return count;
} nally {
db.close();
}
}
// Consulta a lista com todos os carros
public List ndAll() {
SQLiteDatabase db = getwritableDatabase();
try {
// select * from carro
Cursor c = db.query("carro", null, null null, null, null, null, null);
return toList(c);
} nally {
db.close();
}
try
|| ti { 7
// Consulta o carro pelo tipo
public List ndAllByTipo(5F9 IPO) {
Q:_"
SQLiteDatabase db = getwrtbteDtbaS@();
} nally {
db.close();
}
return CI'`0Sj
}
// Executa um SQL
public void execSQL(String sql) {
SQLiteDatabase db = getwritableDatabase();
try {
db.eecSQL(sql);
} nally {
db.close();
}
// Executa um SQL
public void execSQL(String sql, 0bject[] args) {
SQLiteDatabase db = getwritableDatabase();
try {
db.eecSQL(sql, args);
] nally {
db.close();
}
}
Captulo 18 n Persistncia 511
Agora vamos explicar um pouco do cdigo. No Android, as classes que precisam
criar ou abrir um banco de dados devem herdar de SQLite0penHelper.
public class CarroDB extends SQLiteOpenHelper {
Feito isso, no construtor voc deve informar o nome do banco de dados e a verso,
que um cdigo numrico que podemos incrementar.
public CarroDB(Context context) {
super(context, NOME_BANCO, null, VERSAO_BANCO);
}
}
}
- Il ' ll
Nota: recomendvel que para cada tabela crie-se uma coluna com o nome _id
que seja do tipo autoincremento. Dessa forma, ao inserir o registro o valor do
campo " id" omitido, pois gerado automaticamente pelo SQLite.
Depois de criar esse template bsico, dentro da classe voc pode criar os mto
dos que quiser. A lgica sempre consiste em obter o objeto SQLiteDatabase, fazer
S12 Google Android - 4 edio
alguma operao no banco de dados e depois fechar a conexo. A segulf, m05
um template bsico de um mtodo qualquer:
public class CarroDB extends SQLite0penHelper {
Nota: depois de salvar os carros no banco de dados, vamos criar telas para editar
e excluir os carros localmente. Assim podemos brincar com os dados do banco,
e quando quisermos basta atualizar a lista e puxar os dados do web service.
(arroService.java
public class CarroService {
}
S18 Google Android - 4 Ed5
public static List getCarrosFromwebService(Contet context, String tipo)
throws IOException {
String url = URL.replace("{tipo}", tipo);
Log.d(TAG, "URL: " + url);
String json = HttpHelper.doGet(url);
List<Carro carros = parserJSON(contet, json);
// Depois de buscar salva os carros
salvarCarros(Context, tipo, carros);
return carros;
}
} nally {
db.close();
}
17o '7oz- aK
2 r Q W @V
l lm il
.
'" Android Device Monitor
lNam
pl '15
l
j@,,,,,,,
-
. fzx:
a L
i Devifes 53 *Pia rs---l @ Hsfl .l9:~~..z.T*T.EsE;-_ le 23 .9F:-;ef 5Ytt;;_
lj =~ dk'__--_.''_2___@._
"kw 2015-02-O2 10245 l
1l
Mame 2 _,
Android ,W _%z.@.data
[emulator-5554] 2015-01-26
, app 2915020216:18
1
15;43 dr
d,
. c<m.android.systemui ,_app_aSec 2915-01-25 16318 drw:
<;om.android.iauncher
corn.google.android.gms.u ,jj
:_app_b 2015-O1-26
' 157699-private 16:18
2015-01-26 16:18drw;
dnm
com.andrcid_deskclock , Ebackup 2015-02-OZ 15:47 drw;
:om.android.keychain Q bugreports 2015-01-26 16:18 lrwx
comandroid.inputmethod.iatin fj , 5daVk_Cache 2015-01-26 16:18 drw:
com_google.process.gaDPS Yrzfzdata 2015-02-02 15:48 drw:
mm-99le~a"drld'9m5 ' 1/3br.com.livroandroidcarros 2015'02'02 10159 TWJ
com.android.defcontainer ju , ;_app_webVew 2015-02-02 10:50 drw:
com.google.process.location Ecache 2015-01-28 22:47 drw;
l androd.process.meda vgdatabaseg 2015-02-02 10 46 drw:
l Svsemyfves 'i ;llVff);1'1'd.SQl{E 4sos ams-ozoz 1o:4
j: com.android.se|er.telecom
android.process.acore r gles 2015-O2-02
A i Vm_am_|,-Qd_5qzejgumal 11:0510:46
16928 2015-0202 drw:
-rw
comgoogie.android.gms.unstable b 2015~01~28 22:47 lrwx
|1
sqlite>
J Cl
SQLite version 3.8.6 2014-08-15 11:46:33
Enter ".help" for usage hints.
Para testar, fa a um SELECT na tabela dos carros. Ve`a ue deixei a enas trs linhas
para economizar espao no livro, mas 0 SELECT retorna todos os carros da tabela.
sqlite> select _id,nome,tipo from carro;
1|Bugatti Veyronlluxo
2|Ferrari Enzolluxo
3|Lamborghini Reventonlluxo
Para abrir o arquivo, podemos utilizar qualquer software cliente do SQLite, como por
exemplo o SQLite Expert Personal, mas eu costumo utilizar o simples e prtico plugin SQLite
Manager para o Firefox. A figura 18.5 mostra a consulta select * from carro e o resultado.
Captulo 18 I Persistncia
z
. .l z -.row
ea rm; - -7- - f'^' --W '
SQLite Manager - R:\temp\llvro_androd_carros.sqlite
1 Enter SQL E
l, .21 Tucker1948 OTucler foi real... http://www.livro http://hotrodeku... http://www.livroi... -23564224 46.653156 classicos
22 Chevrolet Corvette OChevrolet corv... http://www,livro http://wwwchevr... http://www.lrvroi -23564224 46.653156 classicos
l
l 23 Chevrolet lmpala... O Impala foi lan... http://www.livro... http://ertwikiped http://www.||woi -23564224 -46553156 classicos
i
CarroService.java
public class CarroService {
Portanto, a nica coisa que precisamos fazer repassar esse parmetro booleano
at a classe CarroService, para que l internamente seja possvel decidir se a busca
deve ser feita no banco de dados ou no web service.
CarrosFragment.java
Pronto, depois de atualizar o cdigo dessa forma, por padro todos os carros se
ro buscados do banco de dados. Mas ao fazer o gesto Pull to Refresh os carros sero
buscados do web service e a tabela dos carros ser atualizada com esses registros.
Ao atualizar os dados, voc ver os seguintes logs no LogCat:
D/CarroService:URL: http://www.livroandroid.com.br/livro/carros/carros_classicos.json
D/sql:Deletou [10] registros
D/CarroService: Salvando o carro Tucker 1948 // Aqui vai salvar os outros carros tambm.
/res/menu/menu_frag_carro.xmI
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<iten android:id="@+id/action_video"
android:icon="@drawable/ic_action_video" android:title="Video"
app:showAsAction="always" />
<item android:id="@+id/action_maps"
android:icon:"@drawable/ic_action_map" android:title="Maps"
app:showAsAction="always" />
<item android:id="@+id/action_share"
android:icon="@drawable/ic_action_share" android:title="Share"
app:showAsAction="ifRoom" />
<iten android:id="@+id/action_edit"
android:icon="@android:drawable/ic_menu_edit" android:title="Editar"
app:showAsAction="ifRoom" />
524 Google Android - 4' edio
item android:id="@+id/action_remove"
android:icon="@android:drawable/ic_menu_delete" android:title="Remover"
app:showAsAction=ifRoom" /
Voc vai precisar de algumas imagens, que esto disponveis nos projetos de exem
plo no GitHub do livro. No cdigo da classe, basta inflar o menu do fragment e
tratar os eventos. Por enquanto, vamos mostrar um simples toast ao selecionar as
opes. No se esquea de chamar o mtodo setHas0ptionsMenu(true).
Carrofragmentjava
@Override
public void onCreate0ptionsMenu(Menu menu, Menulnater inater) {
super.onCreate0ptionsMenu(menu, inater);
inater.inate(R.menu.menu_frag_carro, menu);
}
@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_edit) {
toast("Editar: " + carro.nome);
return true;
} else if (item.getItemId() == R.id.action_remove) {
toast("Deletar: " + carro.nome);
return true;
} else if (item.getItemId() == R.id.action share) {
toast("Compartilhar");
} else if (item.getItemId() == R.id.action_maps) {
toast("Mapa");
} else if (item.getItemId() == R.id.action_video) {
toast("Video");
}A
Captulo 18 I Persistncia 525
return super.onOptonsItemSelected(tem);
}
A gura 18.6 mostra o resultado com as aes para visualizar o Vdeo e Mapa, assim
como as opes Share, Editar e Remover no menu action overow
Share
Editar
Remover
A Ferrari FF acaba de ser revelada. Setrata A Ferrari FF acaba de ser revelada. Se trata
do primeiro modelo da marca a ter trao do primeiro modelo da marca a ter trao
integral. Alm disso, ele conta com um integral. Alm disso, eh conta com um
motor dianteiro V12. Se trata de um modelo motor dianteiro V12. Se trata de um modelo
GT de quatro lugares que no s substitui a GT de quatro lugares que no s substitui a
612 mas tambm atrai um novo tipo de 612 mas tambm atrai um novo tipo de
cliente, daquele que gosta de percorrer cliente, daquele que gosta de percorrer
caminhos mais dificeis que exigem trao caminhos mais d iflceis que exigem trao
integ ra li Este modelo revolucionrio (dentro integral. Este modeo revolucionrio (dentro
da marca) tem um novo chassi com entre da marca) tem um novo chassi com entre
eixos maion alm de suspenso eixos maior, alm de suspenso
independente que incorpora a ltima independente que incorpora a ltima
gerao de amortecedores aju stveis, alm gerao de amortecedores austveis, alm
de freios de cer mica da Brembo. de freios de cermica da Brembo.
/res/layout/fragment_editar_carro.mI
<LinearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:layout_width="match_parent" android:layout_heght="match_parent"
526 Google Android - 4 edio
android:padding="16dp" android:orientation="vertical">
<TetView android:tet="Nome"
android:layout_width="match_parent" android:layout_height="wFD_Cte"t" />
<EditTet android:id="@+id/tNome"
android:layout_width="match_parent" android:layout_height="wrD_C"tetH
android:layout_margin="8dp"/>
<Button android:id="@+id/btAtualizar"
android:layout_width="wrap_content" android:layout_height="WFP_C"t@"t"
android:tet="Atualizar" android:layout_gravity="right" />
EditarCarroDiaIog.java
package br.com.livroandroid.carros.fragments;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.Fragmentanager;
import android.support.v4.app.FragmentTransaction;
public class EditarCarroDialog extends DialogFragment {
private Callback callback;
private Carro carro;
private Textview tNome;
// Interface para retornar o resultado
public static interface Callback {
public void onCarroUpdated(Carro carro);
}
ft.addToBackStack(null);
EditafCarroDialog frag = new EditarCarroDialog();
frag.callback = callback;
Captulo 18 u Persistncia
@0verride
public void onStart() {
super.onStart();
if (getDialog() == null) {
return;
}
@0verride
public View onCreateView(Layoutlnater inater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_editar_carro, container, false)
view.ndViewById(R.id.btAtualizar).set0nClickListener(onClickAtualizar());
tNome = (Textview) view.ndViewById(R.id.tNome);
this.carro = (Carro) getArguments().getSerializable("carro");
if (carro != null) {
tNome.setTet(carro.nome);
return view;
}
};
}
/res/values/dimens.xmI
<I'SOUI`CES>
<dimen name="popup_width">300dp
<dimen nane="popup_height">300dp
Para utilizar o DialogFragment que criamos simples, basta chamar o mtodo uti
litrio show() que encapsula o cdigo para criar o fragment. O mais importante
que voc precisa entender que estamos passando como parmetro um objeto
do tipo EditarCarroDialog.Callback, que a interface de retorno (callback) que criei
dentro desse fragment. Essa uma prtica muito comum na Orientao a Objetos
quando um objeto precisa retornar dados para outro. Essas interfaces fazem o meio
de campo entre os objetos. Para continuar, altere o cdigo da classe CarroFragment
conforme demonstrado a seguir: '
) (arroFragment.java
// EditarCarroDialog.java
CarroDB db = new CarroDB(context);
db.save(carro);
if(callback != null) {
callback.onCarroUpdated(carro);
}
A gura 18.7 mostra o alerta com o formulrio para editar o nome do carro e depois
o toast que mostrado quando o usurio clica no boto Atualizar. No cdigo inclu
sive estamos atualizando o ttulo da action bar para reetir o novo nome do carro.
Nome
};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Deletar esse carro?");
builder.setPositiveButton("Sim", dialogClickListener);
builder.setNegativeButton("No", dialogClickListener);
builder.show();
DeIetarCarroDiaIog.java
package br.com.livroandroid.carros.fragnents;
public class DeletarCarroDialog extends DialogFragnent {
private Callback callback;
private Carro carro;
public static interface Callback {
public void onCarroDeleted(Carro carro);
}
public static void show(FragmentManager fm, Carro carro, Callback callback) {
FragnentTransaction ft = fn.beginTransaction();
Fragnent prev = fn.ndFragnentByTag("deletar_carro");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
DeletarCarroDialog frag = new DeletarCarroDialog();
frag.callback = callback;
Bundle args = new Bundle();
args.putSerializable("carro",carro);
frag.setArguments(args);
frag.show(ft, "deletar_carro");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.carro = (Carro) getArgunents().getSerializable("carro");
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialoglnterface.OnClickListener dialogClickListener = new
Dialoglnterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case Dialoglnterface.BUTTON_POSITIVE:
// Deleta o carro
Carropg db = new CarroDB(getActivity());
db.delete(carro);
if(callback != null) {
callback.onCarroDeleted(carro);
}
break;
532 Google Android - 4 edio
case Dialoglnterface.BUTTON_NEGATIVE:
break;
}
1;
AlertDialog.Builder builder = new Alertialog.Builder(getActivity());
builder.setMessage("Deletar esse carro?");
builder.setPositiveButton("Sim", dialogClickListener);
builder.setNegativeButton("No", dialogClickListener);
return builder.create();
}
CarroFragment.java
});
return true;
}
Ferrari FF Teste
AUD: er Spyder
Porsche Panamera
CarrosAppIication.java
Nota: lembre-se de que, quando criamos o projeto dos carros, voc registrou a
classe CarrosApplication no AndroidManifest.xml.
CarroFragment.java
O mtodo onResune() foi escolhido para fazer essa vericao, pois ele executa
do sempre que a activity exibida para o usurio. Consequentemente, todos os
fragments tambm recebem esse evento. Lembre-se de que a tela da lista de carros
tem trs tabs (clssicos, esportivos e luxo), portanto, esses trs fragments da classe
CarrosFragment vo executar o mtodo onResune(). Justamente por isso, passado o
tipo do carro para o mtodo isPrecisaAtualizar(tipo), para atualizar a lista correta.
http://developerandroid.com/guide/topics/manifest/activity-element.htm1#lm0de
Para fazer o backup dos dados, podemos utilizar a classe BackupAgent, na qual preci
samos implementar os mtodos onBackup() e onRestore(), responsveis por transferir
os dados do servidor para a nuvem e restaurar o backup posteriormente. Mas im
plementar essa tarefa no trivial e exige algumas linhas de cdigo. Por esse motivo
existe a classe BackupAgentHe1per, que lha de BackupAgent e faz todo esse trabalho.
conjunto com a classe BackupAgentHe1per, podemos utilizar outras duas classes para
Captulo 18 n Persistncia 537
automatizar o processo de backup. A primeira delas a SharedPreferencesBackupHelper,
capaz de fazer backup do que foi salvo nas preferncias do usurio, e a segunda a
FileBackupHelper, utilizada para fazer backup de arquivos. No aplicativo dos carros,
estamos salvando nas preferncias do usurio SharedPreferences a tab selecionada,
portanto vamos aprimorar o aplicativo e salvar isso na nuvem. Para comear a brin
cadeira, crie a classe EenploBackupAgent. Ateno para o pacote que voc vai utilizar,
pois ser necessrio congurar essa classe no AndroidManiest.xml.
ExempIoBackupAgent.java
package br.com.livroandroid.carros.backup;
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
super.onRestore(data, appVerS0C0d, HGWSGG);
Log.d("backup","Backup restaurado com sucesso.");
l
}
Uma vez que a classe que far o backup est criada, precisamos adicionar as tags
android:allowBackup e android:backupAgent ao arquivo AndroidManifest.xml.
AndroidManifest.xmI
<manifest . . . >
<application android:allowBackup="true"
android:backupAgent=".backup.ExemploBackupAgent" . . >
<meta-data android:name=con.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3RuVsUKmTxDy1Xr3YwRUdD0j9p-tEsebzArJZg" /
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3RuVsUKmTDy1Xr3YwRUdD0j9p-tEsebzArJZg" />
O valor dessa chave nico para sua aplicao e deve ser gerado nesta pgina:
https://developerandroid.com/google/backup/signup.html
Ao abrir a pgina, voc deve ler e aceitar a licena e preencher o campo com o
nome do pacote do seu projeto, que, neste caso, br.com.livroandroid.carros. O re
sultado disso que a chave necessria para a tag ser exibida na tela
e voc poder copiar o valor para seu projeto.
Captulo 18 I Persistncia 539
Pronto, estamos quase l! A congurao do backup j est feita, e agora falta
apenas ad1c1onarmos uma linha de cdigo para executar o backup sempre que
alguma informao for salva nas preferncias. Com esse objetivo, altere a classe
CarrosTabFragment desta forma:
CarrosTabFragment.java
public class CarrosTabFragment extends BaseFragment {
private Backupanager backupManager;
mSlidingTabLayout.set0nPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// Salva o indice da pgina/tab selecionada
Prefs.setInteger(getContext(), "tabId", viewPager.getCurrentItem());
backupManager.dataChanged(); // Faz o backup
}
});
}
O cdigo a seguir mostra como fazer backup dc um arquivo que est salvo na
memria interna. Note que utilizada a classe FleBackupHelper.
Captulo 18 I Persistncia 541
ExempIoBackupAgent.java
public class EemploBackupAgent extends BackupAgentHelper {
@Override
public void onCreate() {
http://developer android.com/guide/topics/ui/settings.html
Android Training -Saving Files
http://developer android.com/training/basics/data-storage/les.html
Android Training -Saving Data in SQL Databases
http://developer android.com/training/basics/data-storage/databases.html
Android Training - Backup API
http://developer android.com/training/cloudsync/backupapi.html
Android Developers - Explicao sobre o atributo IaunchMode=singIeTop
http;//developer android.com/guide/components/tasks-and-back-stack.html
1 ` cAPruLo 19
Action bar de contexto e
`'. compartilhamento
Neste captulo, vamos aprender a utilizar a contextual action bar, mas vamos chama
-la apenas de action bar de contexto ou simplesmente CAB.
Tambm vamos praticar a ao de compartilhamento para enviar informaes
como texto ou fotos para outras aplicaes.
19.1 Introduo
Geralmente, em telas de listagem, o usurio est acostumado a tocar em uma
linha e segurar por mais ou menos dois segundos. Feito isso, o sistema geralmente
mostra algumas opes com um menu utuante ou s vezes at pode criar uma
action bar de contexto.
A gura 19.1, extrada da documentao ocial do Android, mostra duas opes
comuns ao tocar e segurar na lista por mais de dois segundos. No lado esquerdo,
temos o Floating Context Menu e na direita a Contextual Action Bar (CAB).
Criar a primeira opo relativamente simples, e tenho certeza de que, se voc
olhar a documentao ocial, vai conseguir fazer. Mas criar a action bar de con
texto requer um pouco mais de trabalho, por isso vamos estud-la. A action bar
de contexto (CAB) muito utilizada em diversos aplicativos nativos do Android.
Com certeza, ao utiliz-la, deixaremos nossa aplicao mais renada, e o usurio
vai gostar, uma vez que esse um comportamento com o qual ele est acostumado
e espera que seu aplicativo comporte-se da mesma maneira. PorO,eO*1ITl8l
xemp l C 'l
permite selecionar vrias mensagens ao mesmo tempo para exclui-las, e a Galeria
de Fotos permite selecionar vrias fotos para compartilh-las. Vamos implementar
exatamente essa funcionalidade no aplicativo dos carros , para permitir que vrios
carros possam ser excludos ou compartilhados.
542
Captulo 19 I Action bar de contexto e compartilhamento 543
aaaa f
Henry IV (1)
Henry V
Henry Viii
Richard Ill
Merchanl of Venice
Omdm
King Lear
CarroAdapter.java
public class CarroAdapter extends Recyclerview.Adapter {
@Override
public void onBindViewHolder(nal CarrosViewHolder holder, nal int position) {
// Click normal
if (carroOnClickListener != null) {
holder.itemview.setOnClickListener(...);
}
// Click longo
holder.itemView.set0nLongClickListener(new View.0nLongClickListener() {
@0verride
public boolean onLongClick(View v) {
carroOnClickListener.onLongClickCarro(holder.itemView, position);
I`EtUI'I`\ true;
544 Google Android - 4' edio
});
}
Basicamente o que zemos foi detectar o toque longo na view do adapter e chamar o
mtodo onLongClickCarro(View,int) da interface Carro0nClickListener. Lembrando que essa
interface implementada na classe CarrosFragment e por meio dela o fragment recebe
os eventos de clique feitos no adapter. Novamente essa a interface de callback que
j utilizamos em outros exemplos, mas aqui chamei de listener, pois outro nome
comum que esse tipo de interface recebe. Para terminar essa congurao, atualize o
cdigo da classe CarrosFragment para implementar o mtodo onLongClickCarro(View,idx).
a (arrosFragment.java
};
}
A gura 19.2 mostra o resultado ao tocar em um carro e segurar por dois segundos.
Quando o usurio tocar em algum carro da lista e segurar por alguns segundos 7
vamos habilitar a action bar de contexto para mostrar as opes para zxluir ou
compartilhar vrios carros.
Nesse momento, ser possvel selecionar vrios carros ao mesmo tempo conforme
7
mostra a Figura 193. Na figura tambm podems ver que foi feito o cmpqr]h,_
mento dos carros selecionados, e os nomes foram passados por 9MS
Captulo 19 I Action bar de contexto e compartilhamento 545
l trrarr FF
l r.i
Porsche Panarr 1
Lamborghim Av nt do
H ' L _.
Figura 19.2 - OnLongClickListener na lista.
vl ,;j;'_
~ -lmpala Coupe}] _
l
Carros: [Carro{nome='Tucker
1948}, Carro{nome='Chevrolet
- Corvette}, Carro{nome='Chevrolet
l
l |Type message
~ -- Cadilac Deville L; i
Lq-5; f' Convertible
, _ _ , - _ __ ,mw ._ .,__ , ,,. _ ._ .,..._...-.
-:E (arro.java
public boolean selected; // Flag para indicar que o carro est selecionado
}
Para pintar a linha de azul caso o carro esteja selecionado, altere o cdigo do
adapter:
CarroAdapter.java
public class CarroAdapter extends Recyclerview.Adapter {
@0verride
public void onBindViewHolder(nal CarrosViewHolder holder, nal int position) [
j temos o cdigo que vai pintar a linha selecionada de azul, ento chegou o
momento de ativar a action bar de contexto para permitir selecionar os carros.
Com esse propsito, podemos utilizar o mtodo startActionMode(callback) da classe
android.app.Activity, mas como estamos utilizando a biblioteca de compatibilidade
v7, vamos utilizar os mtodos com o prexo support.
CarrosFragment.java
import android.support.v7.view.ActionMode;
public class CarrosFragment extends BaseFragment {
private Actionode actionode;
@0verride
public void onClickCarro(View view, int idx) { . . . }
@0verride
public void onLongClickCarro(View view, int idx) {
if (actionMode != null) { return; }
// Liga a action bar de contexto (CAB)
actionMode = getAppCompatActivity().
startSupportActionMode(getActionModeCallback());
Carro c = carros.get(idx);
c.selected = true; // Seleciona o carro
// Solicita ao Android para desenhar a lista novamente
recyclerview.getAdapter().notifyDataSetChanged();
// Atualiza o titulo para mostrar a quantidade de carros selecionados
updateActionModeTitle();
}
};
}
}
543 Google Android - 4' edio
Dica: o mtodo notifyDataSetChanged() do adapter tem como objetivo iflvlldf 3
lista, forando o Android a redesenhar a lista. Isso utilizado para rClSh3f
a view quando voc adiciona ou remove itens da lista, ou deseja alterar 3 COI' do
carro selecionado, como o nosso caso.
Esta linha de cdigo inicia o modo de ao (action mode), que na prtica signica
que vamos mostrar a action bar de contexto (CAB).
actionMode = getAppCompatActivity().startSupportActionMode(getActionHodeCallback());
Nota: nas prximas citaes, para simplicar o texto, vou chamar o termo action
bar de contexto apenas de CAB, pois a sigla para contextual action bar na
documentao ocial.
CarrosFragment.java
@0verride
@Override
@0verride
recyclerView.getAdapter().notifyDataSetChanged();
}
};
}
Para o cdigo compilar, crie o arquivo de menu que a CAB vai inar.
/res/menu/menu_frag_carros_context.xmI
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share" android:icon="@drawable/ic_action_share"
android:title="Share" app:showAsAction="always" />
<item
android:id="@+id/action_renove" android:icon="@android:drawable/ic_menu_delete"
android:title="Remover" app:showAsAction="always" />
Tambm ser preciso adicionar mais duas propriedades no arquivo stylesxml, respon
svel por congurar o tema da aplicao. Congure o atributo windowActionMode0verlay
para true, a m de posicionar a CAB por cima da Toolbar. Voc pode testar o
aplicativo com e sem essa linha para ver a diferena. Outro atributo necessrio
o actionModeBackground, que dene a cor da CAB, e neste caso deixarei com a cor
primria do aplicativo.
550 Google Android - 4 edio
/res/values/styIes.xml
<iten name:"windowActionMode0ver1ay"true/iten>
iten name="actionHodeBackground">@co1or/prinary
/sty1e>
No se assuste com o tamanho do cdigo para criar o CAB, pois se voc olhar os m
todos da interface ActionMode.Ca11back eles so simples. O mtodo onCreateActionMode()
responsvel por inflar o menu da CAB. Neste menu, vamos mostrar as aes para
Compartilhar ou Remover os carros. O mtodo onPrepareActionMode() chamado sempre
que o menu da CAB invalidado, mas neste exemplo no vamos utiliz-lo. O
mtodo onActionItenC1icked() chamado ao clicar em algumas das opes da CAB
e funciona da mesma forma que o famoso mtodo on0ptionsItemSeiected(item) que
trata os eventos das aes da action bar. O mtodo onDestroyActionMode() chama
do quando a CAB encerrada, e neste momento devemos limpar os recursos,
pois a action bar deve voltar ao seu estado padro. Para encerrar a CAB, deve-se
chamar o mtodo actionMode.nish(). Um bom momento para fazer isso depois
de o usurio escolher alguma das opes.
Neste momento, execute o projeto para conferir o resultado. Ao selecionar um
carro por mais de dois segundos, a CAB ser ativada. No entanto, voc deve ter
percebido um bug ao ativar a CAB, pois somente o primeiro carro selecionado.
Se voc tocar no segundo carro, feita a navegao para a activity de detalhes,
em vez de selecionar a lin_ha.
Para resolver esse problema, altere o mtodo onC1ickCarro(viewI idx) para [estar se 3
CAB est ativada; neste caso, o correto apenas selecionar o carro e redesenhar a lista
Captulo 19 n Action bar de contexto e compartilhamento 551
CarrosFragment.java
public void onClickCarro(View view, int idx) {
Carro c = carros.get(id);
if (actionMode == null) {
Intent intent = new Intent(getContet(), CarroActivity.class);
intent.putExtra("carro", c);
startActivity(intent);
} else { // Se a CAB est ativada
// Seleciona o carro
c.selected = !c.selected;
// Atualiza o titulo com a quantidade de carros selecionados
updateActionModeTitle();
// Redesenha a lista
recyclerview.getAdapter().notifyDataSetChanged();
}
Com essa alterao, a seleo mltipla de linhas vai funcionar quando a CAB
estiver ativada. Caso contrrio, a navegao para a tela de detalhes do carro feita
normalmente.
CarrosFragment.java
@Override
public boolean onActionItenClicked(ActionMode node, Menulten item) {
List selectedCarros = getSelectedCarros();
if (item.getItemId() == R.id.action_remove) {
CarroDB db = new CarroDB(getContext());
try {
for (Carro c: selectedCarros) {
db.delete(c); // Deleta o carro do banco
carros.remove(c); // Renove da lista
}
} nally {
db.close();
552 Google Android - 4 edio
}
Concludas essas alteraes, voc podera ativar a CAB para eXClUlf V21I'l05 CHTYOS
de uma nica vez. Depois de brincar um pouco com isso, recomendo fazer o gesto
de Pull to Refresh para buscar os carros do web service, e restaurar a lista original.
Um padro de interface bem comum no Material Design uma view quadrada
com uma mensagem que aparece na parte inferior da tela. Essa view chamada
de Snackbar, e um exemplo de onde ela utilizada o aplicativo do Gmail. No
Gmail, depois de selecionar e excluir um email, uma mensagem aparece com o
texto ldeleted e a ao UNDO para desfazer a excluso. A boa notcia que a Snaclzbar
faz parte da Android Design Support Library, que pode ser configurada no projeto
por meio desta dependncia:
app/bui|d.gradIe
comple 'com.android.support:desgn:22.2.0'
O cdigo para mostrar uma Snaclebar similar ao utilizado para mostrar um Toast,
conforme demonstrado a seguir. Observe que o primeiro parmetro a view que
serve como ncora para mostrar a mensagem; neste caso utilizamos o Recylerview.
Snackbar.make(recyclerVew, "Carros excludos com sucesso.", Snackbar.LENGTH_LONG)
.setActon("0K", new View.0nClckLstener() {
@0verride
public void onClck(Vew v) {
toast("OK");
}
}).show();
CarrosFragment.java
import android.support.v7.widget.ShareActionProvider;
public class CarrosFragment extends BaseFragment {
public Intent shareIntent;
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Ina o menu especico da action bar de contexto (CAB)
Menulnater inater = getActivity().getMenuInater();
inater.inate(R.menu.menu_frag_carros_contet, menu);
Menultem shareItem = menu.ndItem(R.id.action_share);
ShareActionProvider share = (ShareActionProvider) M
enuItemCompat.getActionProvider(shareltem);
shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/*");
share.setShareIntent(shareIntent);
return true;
}
conforme a gura 193, podemos enviar os carros selecionados por SMS. Veja que
ate o momento a intent de compartilhamento foi configurada para enviar apenas
_ . ~ utura melhoria seria compartilhar
o nome dos carros em modo texto, mas uma f
mais informaoes, como as fotos.
Captulo 19 I Action bar de contexto e compartilhamento 555
19.6 Compartilhando as fotos dos carros selecionados
Para compartilhar as fotos dos carros, precisamos salv-las em arquivos, para na
sequncia criar uma intent com o contedo desses arquivos. Uma maneira de fazer
isso salvar a foto do carro em arquivo no mesmo instante em que mostramos
a foto no adapter, mas desse jeito podemos gastar recursos demais, uma vez que
todas as fotos seriam salvas sem necessidade.
Outra forma , no momento em que o usurio clicar em compartilhar, disparar
uma tarefa (Thread ou AsyncTasl<) para fazer o download das fotos para arquivo,
para somente depois enviar a intent com o compartilhamento. Para o aplicativo
dos carros, vou escolher essa segunda opo. No tpico anterior, pedi para voc
congurar a classe androd . support.v7.wdget.ShareActonProvder no arquivo de menu,
mas desta vez vou pedir para voc remov-la. Portanto, remova essa linha indicada
do arquivo de menu.
/res/menu/menu_frag_carros_context.xmI
<menu . . >
<item androd:id="@+d/acton_share" ...
app:actonProvderC1ass="androd.support.v7.wdget.ShareActonProvider" />
<iten androd:d="@+d/acton_remove" . . . />
CarrosFragment.java
public class CarrosFragment extends BaseFragment {
@0verride
public boolean onActionItemClicked(Actionode mode, Menultem item) {
List<Carro selectedarros = getSelectedCarros();
if (item.getItemId() == R.id.action_remove) {
@0verride
public Object execute() throws Exception {
if(selectedCarros != null) {
for (Carro c : selectedCarros) {
// Faz o download da foto do carro para arquivo
String url = c.urlFoto;
String leName = url.substring(url.lastInde0f("/"));
// Cria o arquivo no SD card
File le = SDCardUtils.getPrivateFile(getContet(),"carros",leName);
IOUtils.downloadToFile(c.urlFoto,le);
// Salva a Uri para compartilhar a foto
imageUris.add(Uri.fromFile(le));
}
rrurn null;
}
Captulo 19 I Action bar de contexto e compartilhamento 557
@Override
public void updateView(0bject o) {
// Cria a intent com a foto dos carros
Intent sharelntent = new Intent();
sharelntent.setAction(Intent.ACTION_SEND);
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
sharelntent.putParcelableArrayListEtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
// Cria o Intent Chooser com as opes~
startActivity(Intent.createChooser(sharelntent, "Enviar Carros"));
}
@0verride
public void onError(Eception e) { alert("0correu algum erro ao compartilhar."); }
@0verride
public void onCancelled(String s) { }
}
.._
_1 E. Y_ wi /'
* Q _. _ " = fat _',1J ^
$ _- qn ..z_ -_ _ , < ,.vz/ .z- ~
K
~f~._'
24
V_z` \_; l i=
.t _ _ _ - _; . ~4 _ 1 ,__f, zh.-'V ,
_fWf
.i\.!fl
M Gmail
.i;!zH,i._ Ut*="=' L z*-<w\="H\'
WhatsApp
11 _
iu.. )I At LI'
L Olive
*To
l
l1
_ Ricardo Lecheta
\
Intents
"l
Neste captulo, vamos fazer uma pausa no desenvolvimento do projeto dos carros
para aprender detalhes importantes sobre a classe androd.content.Intent, que e 0
corao do Android. Uma intent uma mensagem da aplicao para o sistema
operacional, solicitando que algo seja realizado, e representa um importante papel
na arquitetura do Android para integrar diferentes aplicaes.
Cabe ao sistema operacional interpretar essa mensagem e tomar as providncias
necessrias, que pode ser abrir um aplicativo, fazer uma ligao para determinado
nmero, tirar uma foto ou at abrir o browser em uma pgina da internet.
Demonstraremos diversos exemplos de como utilizar a classe Intent e aprendere
mos a chamar algumas aplicaes nativas do Android.
560
Captulo 20 1 |mem5 561
exibir algum endereo, localizao ou rota no Google Maps;
abrir a agenda de contatos para selecionar um contato;
abrir a galeria de fotos ou vdeos para selecionar um arquivo de multimdia;
tocar uma msica ou vdeo;
.
abrir a cmera e solicitar que uma foto ou vdeo sejam gravados;
enviar mensagens de uma tela para outra dentro do mesmo aplicativo, ou
at trocar mensagens entre diferentes aplicativos;
integrar diferentes aplicaes; por exemplo, possvel enviar uma mensagem
para aplicativos especializados em ler um cdigo de barras para obter a resposta;
abrir o Google Play para fazer a instalao de determinado aplicativo;
executar algum processamento pesado em segundo plano usando as classes
BroadcastRecever e Service, que sero explicadas em outros captulos;
e muito mais.
Nota: uma intent entre muitas coisas a forma utilizada para fazer com que
aplicaes em processos diferentes se comuniquem, utilizando uma mensagem
que, dependendo do seu contedo, pode ser interceptada por qualquer aplicao.
Nota: uma intent explcita determina exatamente qual activity deve executar.
Esse tipo de intent utilizado quando a activity ou mensagem deve ser enviada
para alguma classe que faz parte do seu aplicativo. Uma intent implcita uma
mensagem genrica enviada ao sistema peracional e pode ser tratada por
qualquer aplicao instalada.
Acredito que a melhor maneira de entender o que e uma intent e partirmos para
21 PFIICH; POFIZIHIO, abra o prjeto de exemplo deste captulo no /\ndroid Studio
Seguindo mesmozum
'elo contem template de outros projetos
Lstview ` J Vdoilivro,
~ a--activity
2 . .finicial
'. . do
pm
J I com varias opoes. (sida opao t um exemplo de Ifelll
que pode ser enviada ao sistema operacional.
Captulo 20 n Intents
MainActivity.java
package br.com.livroandroid.intents;
public class MainActivity extends AppCompatActivity implements
Adapterview.0nItemClickListener {
private static nal String TAG = "livroandroid";
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] items = new String[]{
"Ligar para telefone", "Discar para telefone",
"Enviar Email", "Enviar SMS",
"Abrir Browser", "Mapa - Lat/Lng",
"Mapa - Endereco", "Mapa - Rota",
"Compartilhar", "Camera Foto",
"Camera Video", "Visualizar Todos Contatos",
"Visualizar Contato 1", "Selecionar Contato",
"Intent customizada", "Intent customizada / schema",
"Sair"
};
ListView listView = new ListView(this);
setContentView(listView);
listview.set0nItemClickListener(this);
listView.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_1, items));
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
switch (position) {
// Ligar para um nmero de telefone
case 0:
Uri uri = Uri.parse("tel:12345678");
Intent intent = new Intent(Intent.ACTION_CALL, uri);
startActivity(intent);
break;
// Discar para um nmero de telefone
case 1:
Uri z Uri.parse("tel:12345678");
intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);
break;
// Enviar um email
Google Android - 4 edio
case 2:
// Email
Intent emaillntent = new Intent(Intent.ACTION_SEND)J _ u
emaillntent.putExtra(Intent.EXTRA_SUBJECT, "Titulo do ell );
emaillntent . putEtra(Intent . EXTRA_TEXT , "Ol" );
emaillntent.putEtra(Intent.EXTRA_EMAIL, "rlecheta@93\-C")3
emaillntent.setType("message/rfc822");
startActivity(emaillntent);
break;
// Enviar um SMS
case 3:
// SMS
uri = Uri.parse("sms:12345678");
Intent smslntent = new Intent(Intent.ACTION_SENDTO,uri);
smslntent.putEtra("sms_body", "0l");
startActivity(smsIntent);
break;
// Abrir o browser em uma pgina
case 4:
// Browser
uri = Uri.parse("http://google.com");
intent = new Intent(Intent.ACTION_VIEH, uri);
startActivity(intent);
break;
// Abrir o mapa na coordenada: Latitude/Longitude
case S:
// Mapa
String GEO_URI = "ge0:-25.4@89185,-49.3222402";
intent = new Intent(Intent.ACTION_VIEH, Uri.parse(GEO_URI));
startActivity(intent);
break;
// Abrir o mapa em um endereo
case 6:
// Mapa
GEO_URI = "geo:G,0?q=Av. Manoel Ribas - Santa Felicidade,
Curitiba - Paran, Brasil";
intent = new Intent(Intent.ACTION_VIEw, Uri.parse(GEO URI));
startActivity(intent);
break;
// Abrir o mapa e mostrar a rota entre dois endereos
case 7:
String rota =
Captulo 20 1 Intents
"httpz//maps.google.com/maps?saddr=-25.4089185,-49.3222402&dad
dF=-2S.428781, -49.30925";
intent = new Intent(Intent.ACTION_VIEw, Uri.parse(rota));
startActivity(intent);
break;
// Compartilhar informaes
case 8:
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
sharelntent.putEtra(Intent.EXTRA_SUBJECT, "Compartilhar");
shareIntent.putEtra(Intent.EXTRA_TEXT, "Bla bla bla");
startActivity(sharelntent);
break;
// Abrir a cmera para tirar uma foto
case 9:
Intent fotolntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(fotoIntent, 9);
break;
// Abrir a cmera para gravar um video
case 10:
Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE)
startActivityForResult(videolntent, O);
break;
// Abrir a agenda para visualizar todos os contatos
case 11:
uri = Uri.parse("content://com.android.contacts/contacts");
intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);
break;
// Abrir a agenda para visualizar um contato
case 12:
uri = Uri.parse("content://com.android.contacts/contacts/1");
intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);
break;
// Abrir a agenda para escolher um contato
case 13:
uri = Uri.parse("content://com.android.contacts/contacts");
intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 13);
break;
// Intent com ao customizada
case 14:
566 Google Android - 4 edio
intent = new Intent("br.com.iivroandroid.intents.TESTE");
startActivity(intent);
break;
// Intent com ao customizada especicando um 'schema'
case 15:
// INTENT_FILTER
uri = Uri.parse("livroandroid://carros/ferrari");
intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);
break;
default:
nish();
break;
}
} catch (Exception e) {
Toast.makeTet(this, "Erro 1" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
@0verride
protected void onActivityResult(int codigo, int resultado, Intent it) {
Log.i(TAG, "Menu.onActivityResult: " + codigo + ", resultado: " + resultado + " > " + it);
if (codigo == 9 && resultado == Activity.RESULT_0K) {
// Tirou a foto, aqui podemos ler o Bitmap
}
Neste cdigo, temos vrios exemplos de intents implcitas que sero enviadas como
mensagem para o sistema operacional. Cada intent tem como objetivo realizar algu
ma ao, como abrir o browser, abrir o mapa, ligar para um telefone etc. Recomendo
que voc execute este projeto no emulador ou em um dispositivo para conferir o
resultado. Veja que o cdigo-fonte est comentado, portanto leia com calma.
Como essa uma intent implcita que tem uma ao genrica, alguma aplicao
deve entender o que signica essa ao. Depois vamos estudar mais detalhes
sobre isso, mas, apenas para adiantar o assunto, essa intent vai abrir a activity
TesteActivity. Isso acontece porque no manifesto da aplicao foi congurado um
ltro de inteno, conforme mostra o trecho de cdigo a seguir:
// AndroidManifest.xml
<activity android:name=".TesteActivity" . . >
<intent-lter>
<action android:name="br.con.Iivroandroid.intents.TESTE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-1ter>
Enviar E-mall
Enviar SMS
Abrir Browser
Mapa - Lat/Lng
Mapa - Endereco
Mapa - Rota
20.4 Permisses
Algumas intents precisam de permisses para funcionar, como nos casos de fazer
ligao, listar os contatos da agenda e abrir o browser. Portanto, este breve tpico
serve apenas para alert-lo de que essas permisses foram configuradas no projeto
de exemplo deste captulo.
<uses-permission androd:name="androd.permsson.CALL_PHONE" />
<uses-permission androd:name="androd.permission.READ_CONTACTS" />
<uses-permission androd:nane="androd.pernsson.INTERNET" />
pois este captulo apenas uma breve introduo sobre as intents. Posteriormente.
teremos captulos dedicados aos recursos de multimdia e agenda de contatos/
Sobre o exemplo que tein como objetivo selecionar um contato, utilizada uma Uri
Captulo 20 1 Intents 569
customizada do provedor de contedo dos contatos e a ao Intent.ACTION_PICK, que
por conveno indica que a intent deve retornar algum resultado.
// Abre a agenda para selecionar um contato
Uri uri = Uri.parse("content://com.android.contacts/contacts");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 13);
Neste exemplo, a agenda de contatos vai abrir, logo cadastre alguns contatos no
emulador para testar. Depois que voc selecionar algum contato, ele ser enviado
como resultado para a activity de origem, ou seja, a MainActivity no nosso caso. O
resultado enviado para o mtodo onActivityResult( . . .) que deve ser sobrescrito na
activity que fez a chamada da intent. No trecho de cdigo citado, o cdigo=13 foi
o utilizado para chamar a activity dos contatos. Nesse contexto estamos testando
se o retorno dessa activity e se o resultado RESULT_0K.
protected void onActivityResult(int codigo, int resultado, Intent it) {
int resultado Constante que indica se o resultado foi bem sucedigq ou flizam se
ser qualquer constante denida na apl1caaO. POV Pa mo* U l
as constantes Actvty.RESULT_OK e RESULT_CANCELED
intent Intent que originou o retorno. Com essa mtent e possivel recup
Bundle para ler os parmetros retornados. O metodo tntet-9fD()
retorna uma Uri, que algumas aplicaes utilizam para enviar a resposta.
20.6 IntentFiIter
Quando uma mensagem enviada ao sistema operacional utilizando a classe
Intent, necessrio congurar a classe IntentF1ter para interceptar essa mensagem,
com base em seu contedo. Por exemplo, uma das intents que fazem parte do
projeto de exemplo deste captulo esta:
// Este cdigo abre a TesteActivty, mas por qu?
Intent intent = new Intent("br.com.livroandrod.intents.TESTE");
startActvity(ntent);
Ao enviar essa mensagem, o Android abrir a activity que foi congurada para in
terceptar a ao. Essa congurao deve ser feita com a tag <ntent-fi1ter> no arquivo
de manifesto. A seguir podemos visualizar o cdigo do arquivo AndroidManiest.xml
do projeto deste captulo. Veja que a activity TesteActvty foi congurada para
interceptar exatamente a ao br.con.1vroandroid.intents.TESTE.
AndroidManifest.xmI
<?m1 verson="1.0" encoding="utf-8"?>
<manfest _ . .>
<app1cation . . . >
<activty androd:nane=".ManActvty" . . >
<activty androtd:nane=".TesteActvity" . . .>
ntent-1ter>
action androd:nane="br.con.1vroandrod.intents.TESTE" /
<category android:nane="android.intent.category.DEFAULT" ]
</ntent-1ter>
<intent-1ter>
<action android:nane="android.ntent.action.VIEw" />
<category androd:name="android.ntent.category.DEFAULT" />
<data androd:scheme="1vroandrod" />
</ntent-1ter>
</activty>
</applcation>
Captulo 20 1 Intent; 571
Por isso, ao disparar a intent com a ao "br.con.ltvroandroid.intents.TESTE",
a classe TesteActtvtty executada. A tag <tntent-1ter> corresponde classe
android.content.IntentFt1ter, que como voc j deve ter entendido utilizada para
mapear uma ao para determinada classe. Seguindo o mesmo raciocnio, se a
mensagem "bingo" for enviada, tal como no exemplo a seguir:
Intent intent = new Intent("bingo");
startActivity(intent);
Basta declarar o seguinte ltro em qualquer activity para receber essa mensagem.
<intent-1ter>
<action android:name="bingo" />
<category android:name="android.intent.category.DEFAULT" />
</intent-1ter>
Nota: apenas para reforar o conceito, note que nunca declaramos um ltro
para intents explcitas, pois elas especicam exatamente qual activity deve
abrir. Filtros so utilizados apenas para mapear aes genricas, no caso de
intents implcitas.
.__,
androd.ntent.category.LAUNCHER - Categoria que faz com que esta activity
que presente na tela inicial do Android, ou seja, esta activity o ponto de
partida da aplicao e pode ser iniciada pelo usurio.
Agora que j estudamos o bsico sobre o que uma mensagem enviada pela
intent e aprendemos a interceptar essas mensagens com um intent filter vamgg
criar um exemplo customizado de intent para abrir a lista de carros
.1.
O exemplo que vamos criar parecido com a intent que mostra roda 3 lista dg
contatos e permite se ecionar um contato, de forma que o contato selecionado
retornado para a aplicao. Apenas para lembrar, essa intent dos contatos pode
ser disparada com este cdigo:
Captulo 20 1 Intents 573
// Abre a agenda para selecionar um contato
Uri uri = Uri.parse("contentz//com.android.contacts/contacts");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 13);
Antes de comearmos, vale ressaltar que esse tipo de intent muito especca, e
dificilmente voc vai precisar fazer este tipo de coisa no seu dia a dia. Ao desen
volver aplicativos, geralmente voc navega entre as activities utilizando intent ex
plcitas. Estamos estudando isso porque quero que voc entenda como o Android
funciona por debaixo dos panos.
Outro ponto que voc precisa entender que estas mensagens podem ser dispara
das de qualquer aplicao instalada. No projeto dos carros, vamos configurar um
ltro de inteno (intent-filter) para interceptar essas mensagens. Portanto vamos
abrir uma porta na aplicao dos carros, para que ela possa fornecer contedo,
de modo a comunicar-se com outras aplicaes. Para continuar, faa uma cpia
do projeto dos carros e crie a seguinte activity:
574 Google Android - 4 ed
1 CarroslntentActivity.java
package br.com.livroandroid.carros.activity;
}
Captulo 20 I Intents 575
} nally {
db.close();
}
@0verride
public void onLongClickCarro(View view, int idx) {
// nada aqui
}
};
}
Essa activity busca todos os carros que esto salvos no banco de dados, e para
simplificar o cdigo note que no utilizei nenhuma thread (AsyncTasl<), mas na
prtica isso seria necessrio. Veja que adicionei algumas anotaes no cdigo,
explicadas logo a seguir.
Na parte (*1*) estamos extraindo as informaes da intent e de sua Uri. Com base
nessas informaes, podemos decidir o que fazer.
Log.d("livroandroid", "Actionz " + intent.getAction()); // Imprime ACTION_VIEw ou ACTION_PICK
Log.d("livroandroid", "Schemez " + uri.getScheme()); // Imprime carro://
Log d("livroandroid","Host: " + uri.getHost()); // Imprime br.com.livroandroid
Log.d("livroandroid","Path: " + uri.getPath()); // Imprime /carros ou /carros/nome
Isso feito para extrair as partes da Uri utilizada pela intent, que carros://br.com.
livroandroid.carros/carros oiicarros://br.com.livroandroid.carros/carros/nome_carro.
A parte (*2*) o cdigo que mostra todos os carros em uma lista com o Recyclerview,
e isso feito se o mtodo uri.getPath() igual a /carros. A parte (*3*) o cdigo
que l o nome do carro, extraindo do path da Uri. Neste caso, o carro buscado
no banco de dados pelo nome, e se for encontrado a activity de detalhes do carro
576 Google Android - 4' edio
chamada. A parte ('4') o cdigo que trata o evento de seleo da ISIQ, TICSC
caso criada uma intent de resultado, com os parmetros que so o I'iOm 6 8
foto do carro. O metodo setResu1t(codigo,ntent) e utilizado para enviar a ltt de
retorno. Sendo assim, quando o metodo finsh() for chamado, a activity que fez a
chamada da intent receber o resultado no mtodo onActvtyResu1t(. - - )
listas explicaes so sucintas, pis espero que voc d uma boa estudada no
cdigo para melhorar seu aprendizado. Vamos continuar com a conguraao.
(lomo essa activity precisa abrir ao receber determinada mensagem, congure o
arquivo de manifesto conforme demonstrado a seguir:
ii AndroidManifest.xmI
/res/layout/activity_main.xmI
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
androd:padding="16dp" androd:orientation="vertica1">
<TetView
android:tet="@string/he11o_wor1d"
androd:1ayout_width="wrap_content" android:1ayout_height="wrap_content" />
<Button
android:onC1ck="onC1ickMostrarCarroPe1oNome"
android:layout_width="wrap_content" android:layout_heght="wrap_content"
android:text="Mostrar carro pelo nome" />
<Button
androd:onC1ck="onC1ckSe1econarCarro"
android:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
android:tet="Se1econar um carro" />
<EdtText
androd:d="@+d/tNomeCarro"
android:1ayout_width="match_parent"
android:1ayout_heght="wrap_content"
androd:hint="@string/nome_carro_selecionado"/>
<ImageVew
androd:d="@+d/mgFotoCarro"
android:1ayout_wdth="match_parent"
androd:1ayout_height="0dp"
android:1ayout_weight="1"
android:layout_margin="10dp"/>
MainActivity.java
import con.squareup.picasso.Picasso;
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
}
@0verride
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == Activity.RESULT_0K && requestCode == 99) {
// L as informaes do carro selecionado
String none = data.getStringEtra("nome");
String url_foto = data.getStringEtra("url_foto");
Log.d("livroandroid","Fotoz " + url_foto);
// Mostra os dados do carro selecionado
EditText text = (EditText) ndViewById(R.id.tNomeCarro);
Inageview img = (lmageview) ndViewById(R.id.imgFotoCarro);
tet.setText(none);
Picasso.with(this).load(url_foto).into(ing);
}
Para mostrar a foto do carro, a activity utiliza a biblioteca Picasso' logo adicione
essa dependncia no arquivo app/build.gradle conforme zemos no projeto dos
carros. Como preciso acessar a internet ar af oto
p a mostrar f do carro, configure
a permisso no arquivo AndroidManifest.xml.
Captulo 20 I Intents 579
AndrodManifest.ml
<manifest . . . />
<uses-permission androd:name="androd.permssion.INTERNET" />
<applcaton ... />
</manfest>
Ao executar esse projeto no emulador, voc ver uma tela com dois botes. Ao
clicar no boto Mostrar(arro pelo Nome, a ao ACTION_VIEN com a Ur "carros://br.com.Iivroandroid.
carros/carros/Ferrari FF" ser disparada. O resultado ser como a gura 20.2. Veja que
interessante! Uma aplicao enviou uma mensagem para outra!
i Hello world!
l sEi_ec|oNAR um cARRo
<: .
Figura 20.3 - Intent customizacla para selecionar um carro.
Faa o teste! Dispare uma intent como essa e veja o erro que vai acontecer.
startActivity(new Intent("NAO EXISTE"));
Para vericar se a intent pode ser tratada por alguma aplicao, podemos utilizar
a seguinte classe:
IntentUtiIs.java
package livroandroid.lib.utils;
public class IntentUtils {
public static boolean isAvailable(Context ctx, Intent intent) {
List list =
nal PackageManager mgr = ct.getPackageManager();
l
}
Captulo 20 I Intents 531
Assim, antes de disparar uma intent, o recomendado sempre vericar se ela
pode ser tratada, conforme mostra este cdigo:
public void onCiickSe1ecionarCarro(View view) {
Uri uri = Uri.parse("carros://br.con.1ivroandroid.carros/carros");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
if(IntentUti1s.isAvai1ab1e(this,intent)) {
startActivityForResu1t(intent, 99);
}
Se voc j entendeu, sabe que basta declarar um ltro com o <data android : schene="http" />,
conforme demonstrado a seguir.
<intent-1ter>
<action android:nane="android.intent.action.VIEw" />
<category android:name="android.intent.category.DEFAULT" />
<data android:schene="http" />
</intent-1ter>
582 Google Android - 4 edio
__ .--
,~_.
, . a,
z ~sia
ualaodis arara
a licato de
laaa o teste! be voce declarar esse filtro em qualquer activity L , P
. - , ,I ` . i ~ z resultado.
llltllt para abrir o browser. o Android vai lbe perguntar com Cl P S*
browser voce descia abrir o conte udo. /\ figura 20.4 mostrt o
Enviar Email
Enviar SMS
Abrir Browser
Mapa - Rota
Bem, antes que voc pergunte, no e possvel dar prioridade para nenhuma apli
cacao. Sendo assim, sempre que existirem duas ou mais aplicaes que podem
tratar a mesma mensagem, o Android vai perguntar ao usuario para decidir qual
aplicacao ele deseja abrir. Lembre-se: e o usuario que manda no celular dele, e
nao voc. Para dar outros exemplos, possivel interceptar o botao que cbama a
activity que faz a ligao.
<ntent-lter>
<acton androd:name="androd.ntent.acton.CALL_BUTTON" />
<category androd:name="android.ntent.category.DEFAULT" />
</ntent-1ter>
'iztmizm podemos interceptar o toque no boto lrlome, e criar uma tela inicial
ttaliiieiite (llS()llll71Ll.
Captulo 20 1 Intents 533
<ntent-1ter>
<acton androd:nane="androd.ntent.acton.MAIN" />
<category androd:nane="android.ntent.category.HOME" />
<category androd:nane="android.ntent.category.DEFAULT" />
</intent-1ter>
Insira esses ltros em alguma activity para testar e veja que interessante. Natu
ralmente, isso serve apenas para voc entender o conceito da plataforma, pois
necessrio um motivo muito bom para substituir uma aplicao nativa.
Um jeito melhor ainda de disparar essa intent verificar se o aplicativo est ins
talado. Caso no esteja, vamos abrir o Google Play para o usurio instalar. Veja
que tudo feito por intents.
534 Google Android - 4 edio
public void onClick(View v) {
Intent intent = new Intent("con.google.zing.client.android.SCAN");
if(IntentUtils.isAvailable(contet,intent)) {
startActivityForResult(intent, O);
} else {
Toast.nakeTet(contet,"Instale o app Barcode Scanner",Toast.LENGTH_SHORT).show();
intent = new Intent(Intent.ACTION_VIEw);
intent.setData(Uri.parse("narket://details?id=com.google.zing.client.android ));
startActivity(intent);
}
Deixo como exerccio para voc construir esse aplicativo, ou caso prera abra o
projeto de exemplo deste captulo. O nome do projeto HeIIoBarCodeReader.
Este breve tpico serve apenas para alert-lo da importncia do padro de nomes de
uma intent. Por conveno, o nome do pacote da aplicao sempre colocado antes
da ao, com o objetivo de diferenciar as intents de diferentes aplicaes. No apli
cativo dos carros mostramos como abrir uma activity com base na seguinte intent:
// Abre o carro pelo none
Ur uri = Uri.parse("carros://br.com.1ivroandrod.carros/carros/Ferrari FF");
Intent intent = new Intent(Intent.ACTION_VIEw, ur);
startActivity(ntent);
Neste caso estamos utilizando a constante Intent.ACTION_VIEII de uma ao nativa
do Android, mas essa ao tambm segue a mesma conveno, pois sua string
android.ntent.acton.VIEw.
http://developer android.com/guide/components/intents-lters.html
API Guides - Common Intents
http://developer android.com/guide/components/intents-common.html
Android Training - Sending the User to Another App
,8
Segundo a documentao ocial do Android, os seguintes formatos so suportados
ao desenvolver as aplicaes.
udio - mp3, midi, 3gp, ogg, m4a, wav e ac.
Vdeo - mp4 e 3gp.
Os protocolos de comunicao suportados so HTTR HTTPS e RTSR O Real Time
Streaming Protocol (RTSP) extremamente recomendado no caso de streaming
de vdeos. Para mais informaes consulte o site ocial:
http://developerandroid.com/guide/appendix/media-formats. html
- no e so
Tocar uma msica simples, basta congurar a origem do arquivo com al uma
verso do metodo setDataSource(...), chamar o mtodo prepare() a m de prepa
rar a mdia e na sequncia o mtodo start() O mtodo prepare() sncro '
retorna quando o arquivo estiver pronto para reproduo. Depois que a mdia
586
Captulo 21 I Multimdia - udio, vdeo e cmera 587
estiver pronta para ser reproduzida, basta chamar o mtodo start() para iniciar
a msica ou vdeo.
dl/f mw
K Prepared N)
Stern zzmo
prop|reA:yncO V. A
.` ' r /-f"""` "`~\ __ _ \
Q }~F>.n~h. v_r (L Started Iioyl/1giTu,ue&&
} AePk,[.OOm`nt.\_"M_ " r PIY ac completo:
r- ,J,slopg
-` lldx
igzmf1
_lll.Q
1 * `
.S1StPPd
` 1.xI E,/"
EQ " ,, _
_;_`:cckTo _\\
O/p
df' as s e Pmucd
aus:O
I-Oping = false &&
`\ _ onCompleonO invoked on
:topO \. \ 1 strtO
\ OnComplet1onL\sten?r 1
\\\ (note. lonx begrmnm)
PIayerMp3
/~k*
* @see android.media.MediaPlayer.0nCompletionListener#onCompletion(android.media.MediaPlayer)
*/
public void onCompletion(MediaPlayer mp) {
Log.d(CATEGORIA, "Fim da msica: " + mp3);
}
Observe que essa classe controla o estado de reproduo da mdia. Para isso, o
atributo status da classe utilizado, de modo que a sequncia correta dos mtodos
seja chamada, respeitando o diagrama de estados da classe MediaPlayer. Para tal, o
mtodo start(mp3) verica o estado atual antes de iniciar a reproduo.
O prximo passo criar o arquivo de layout, que contm um campo de texto
com o caminho do arquivo mp3 e os botes start, pause e stop para controlar o udio.
[] /res/layout/activity_main.mI
<?xml version="1.G" encoding="utf-8"?>
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match parent"
android:orientation="vertical" android:padding="16dp">
Captulo 21 I Multimdia - udio, vdeo e cmera 591
<TetView
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Player MP3" />
<EditText
android:id="@+id/arquivo"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:text:"/storage/sdcard/Music/linkin_park1.np3" />
<LinearLayout
android:layout_width="match_parent" android:layout_height="wrap_content" >
<ImageButton
android:id="@+id/start"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/img_start" />
ImageButton
android:id="@+id/pause"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/img_pause" />
ImageButton
android:id="@+id/stop"
android:layout_width="wrap_content" android:layout_height="wrap_content
android:src="@drawable/ing_stop" />
A seguir, podemos visualizar a activity que usar esse arquivo de layout e tratar
os eventos dos botes para tocar a msica. Todo o trabalho, na verdade, delegado
para a classe PlayerMp3 que criamos anteriormente.
MainActivity.java
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
Toast.makeTet(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
// Libera recursos do MediaPlayer
p1ayer.c1ose();
}
Nota: neste exemplo, se voc tocar a msica e fechar a tela da activity, o P1ayerMp3
ser encerrado, pois ele est atrelado ao ciclo de vida da activ-ity. No captulo
`dd
27, sobre a classe Service, vamos aprender a executar processos em segundo
plano que podem car vivos no sistema operacional do Android. Utilizando
esses servios e segun o plano, a msica continuar executando mesmo ao
fecharaaphcao.
Captulo 21 I Multimdia - udio, vdeo e cmera 593
Pizzyzzf MP3
||nkn__park1 .mp3
lfstorge/sdcard/Musc/ z
b M D * Q_=:sdc:ard
' P IB Alarms
* E Android
Hocim
i > EB Download
1 V IB LOSIDIR
l
/res/Iayout/actvity_main.xml
<?ml verson="1.G" encodng="utf-8"?>
<LnearLayout androd:orentaton="vertca1" . . .>
// EdtText para digitar o caminho do vdeo aqui
// Botes de play, pause e stop aqui
// Por ltimo o Vdeovew
<VdeoVew android:id="@+d/VdVeW"
androtd:1ayout_width="match_Df"t"
androdjayout_heght="0dp" androd:layout_weght="1" />
</LnearLayout>
594
_ . _ , ' ' ' a dife Google Android - 4 edio
MainActivity.java
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
Toast.nakeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
@0verride
protected void onDestroy() {
super.onDestroy();
Captulo 21 1 Multimdia - udio, vdeo e cmera 595
// Libera recursos do MedaP1ayer
vdeoview.stopP1ayback();
}
U* IE DCIM
U* EE: Download
l
P E5 LQST.DlR
. ' E.= Movies
l
last_mohicans.3gp
"E3hdusic
linkin_park1.mp3
/res/menu/menu_popup_video.xmI
<menu m1ns:androd="http://schemas.android.com/apk/res/android"
m1ns:app="http://schemas.androd.com/apk/res-auto">
<tem android:id:"@+id/action_vdeo_browser"
android:title:"@strng/acton_video_browser" app:showAsActon="a1ways" />
<item androd:id:"@+id/acton_vdeo_p1ayer" android:title:"@string/action_vdeo_p1ayer"
app:showAsAction="a1ways" />
<tem androd:id:"@+d/acton_vdeo_videoview"
android:title:"@string/acton_vdeo_vdeoview" app:showAsActon="a1ways" />
/res/values/strings.xmI
<?xm1 verson="1.0" encoding="utf-8"?>
<string name:"acton_video_browser">Browser</strng>
<strng name="action_video_p1ayer">Vdeo P1ayer
<string name="acton_vdeo_videovew">vd@Qvew</5tring>
Captulo 21 I Multimdia - udio, vdeo e cmera 597
Agora que criamos o arquivo de menu, vamos alterar o cdigo que trata o
evento da action bar para, ao selecionar o item Vdeo, mostrar as opes para o
usurio com a ajuda da classe PopupMenu. Observe que o cdigo utiliza a classe
android.support.v7.widget.PopupMenu da biblioteca de compatibilidade.
(arroFragment.java
import android.support.v7.widget.PopupMenu;
import livroandroid.lib.utils.IntentUtils;
public class CarroFragment extends BaseFragment {
});
popupMenu.show();
}
}
return super.onOptionsItemSelected(item);
}
}
598 Google Android - 4 edio
, . . . - ' ' ndroid-utils
.-_oe
Para o codigo compilar, importe a classe Intenttils da biblioteca a ,
strar o vdeo no
pois ela contm os mtodos para disparar as intents, a m de mo I d
browser ou no player nativo. Para sua consulta, o codigo fonte ClSS3 C 355 P
ser visualizado a seguir:
IntentUtiIs.java
package livroandroid.lib.utils;
public class IntentUtils {
private static nal String TAG = "IntentUtils";
Preo de USS 1,5 milho, Ferrari Enzo tem Preo de US S 1,5 milho, Ferrari Enzo tem
fascinado muitos amantes do desporto fascinado muitos amantes do desporto
automvel em todo o mundo. O carro possui automvel em todo o mundo. O carro possui
um motor de 12 cilindros. Apenas 399 um motor de 12 cilindros. Apenas 399
carros foram produzidos sempre e Michael carros foram produzidos sempre e Michael
Schumacher no se esquea de comprar Schumacher no se esquea de comprar
um. A tecnologia de Frmula 1 fez este lindo um. A tecnologia de Frmula 1 fez este lindo
carro de luxo uma escolha favorita entre os carro de luxo uma escolha favorita entre os
amantes de carros Ferrari. Caracteristicas amantes de carros Ferrari. Caracteristicas
como a aerodlnmlca poderosa, o corpo de como a aerodlnmlca poderosa, o corpo de
bra de carbono e os discos de freio de fibra de carbono e os discos de frelo de
cermica de carbono torna-lo especial entre cermica de carbono torn-lo especial entre
todos os carros de luxo. todos os carros de luxo.
Mas caso saiba que se trata de uma URL de vdeo, a opo do player de vdeo
nativo mais interessante, pois dispara uma intent que ser tratada pela aplicao
nativa de vdeo. Vale relembrar que, se mais de uma aplicao conseguir tratar a
mensagem, o Android vai perguntar ao usurio qual aplicao deve executar. A
gura 21.5 mostra um vdeo sendo tocado no player nativo do emulador.
. . - _z .' f^ .'cisar
" ' ` arar uma intent e
21.6 Utilizando o VideoView no projeto dos carros
- , zmostrar o vdeo
A forma mais simples de mostrar um video na apllC21a0 6 d1SP
- -- ' w.
utilizar o player nativo. Se, porem, por algum motivo voce Pre
dentro do layout da apl1caao,e necessario utilizar a classe V1d@0V1
Como j estudamos a classe Videoview, vamos s exercitar o conceito mais uma
vez e criar uma nova activity e fragment para tocar o vdeo do carro. Crlc uma
activity conforme demonstrado a seguir. Note que ela recebe o ob]eto carro por
parmetro e delega o trabalho para um fragment.
VideoActivty.java
package br.com.livroandroid.carros.activity;
import android.support.v4.app.NavUtils;
import br.com.livroandroid.carros.fragments.VideoFragment;
public class VideoActivity extends BaseActivity {
private Carro carro;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
// Congura a Toolbar como a action bar
setUpToolbar();
carro = (Carro) getIntent().getSerializableEtra("carro");
getSupportActionBar().setTitle(carro.nome);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
// Adiciona o fragment ao layout da activity
VideoFragment videoFragment = new VideoFragment();
videoFragment.setArguments(getIntent().getEtras());
getSupportFragmentManager().beginTransaction().replace(R.id.fragLayout,
videoFragment).commit();
}
@0verride
return super.on0ptonsItemSe1ected(tem);
}
AndroidManifest.xmI
<app1icaton . . .>
<actvty androd:name=".actvity.VideoActivty"
androd:label:"@string/tt1e_actvty_vdeo"
androd:parentActvtyName=".activity.CarroActvty" >
<meta-data androd:name="androd.support.PARENT_ACTIVITY"
android:va1ue=".activty.CarroActivity" /
</actvty>
</applcaton>
Se voc estiver na activity E, como fazemos para voltar para a activity B? Neste caso, se
utilizarmos o mtodo nsh(), apenas a activity E ser nalizada e a activity D passar
a ocupar o topo da pilha. Mas o que queremos derrubar todas as activities da
pilha, at chegar na B. Utilizamos ento o ag FLAG_ACTIVITY_CLEAR_TOP na intent, mas
a classe NavUt1s contm mtodos justamente para facilitar esse tipo de navegao.
Vamos continuar com o cdigo para mostrar o vdeo do carro. A seguir, podemos
ver o arquivo de layout da activity VdeoActvty, que contm apenas a Toolbar e
um FrameLayout para adicionar o fragment.
602 Google Android - 4 edio
/res/Iayout/attivity_vdeo.xmI
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="mtCh_Pfe"t" >
<include layout:"@layout/include_toolbar" />
<FrameLayout
android:id="@+id/fragLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_video" />
Mais uma vez, estamos utilizando a activity para controlar a navegao das telas,
mas internamente todo o contedo e lgica cam no fragment. A classe VideoFragment
precisa ler o objeto carro que enviado pelos argumentos e congurar o Videoview
com a URL do vdeo.
VideoFragment.java
package br.com.livroandroid.carros.fragments;
import android.widget.MediaController;
videoView.start(); '
videoview.setHediaController(new MediaController(getContext()));
return view;
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<VideoView android:id="@+id/videoview"
android:layout_width="match_parent" android:layout_height="match_parent" />
Agora que j criamos a activity e o fragment que vai mostrar o vdeo, volte classe
CarroFragment e altere o trecho de cdigo do PopupMenu cujo alerta inflamos com as
opoes. Na opo do Videoview, vamos navegar para a activity que acabamos de criar.
CarroFragment.java
public class CarroFragment extends BaseFragment {
popupMenu.set0nMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.action_video_browser) { . . .}
else if (item.getItemId() == R.id.action_video_player) { ... }
else if (item.getItenId() == R.id.action_video_videoview) {
// Abre outra activity com Videoview
Intent intent = new Intent(getContext(), VideoActivity.class);
intent.putExtra("carro", carro);
startActivity(intent);
I'`lU` true;
}});
}
Feito isso est pronto. Desta vez, ao selecionar a opo Vdeo e depois o item VideoView,
7
vamos navegar para a activity do vdeo que acabamos de criar. A gura 21.6 mostra
o resultado com o vdeo sendo mostrado no Videoview.
Com relao ao projeto dos carros, isso tudo, pois j mostrei trs maneiras para
mostrar o vdeo do carro. Particularmente, sempre tento convencer meus clientes
de que a melhor opo disparar uma intent e utilizar o prprio player nativo.
motivo simples, pois mais fcil de fazer e o resultado emuito melhor. Mas e
claro que tudo depende do caso. Por exemplo, em uma aplicao para tabletem
que tgmog um grande espao disponvel na tela, muitas vezes opta-se por utilizar
um Videoview, a m de aproveitar melhor o espao das views dentro do layout.
604 Google AndroId 4" !d|
ov
\.
!':_\;:n. .Tl. \ |J'1..unm \`:.I.\: u
Sc \`u\ .u h.| qm' |IU*'il 11\'n'f`za ~.l|v ;\- IIIFIII Hmh.: lu ;',.||\ .nn Inn, '~.|.\ *n;'_.1
Ihltl. |\U|H. UHHH \|lf~r' HU \`;I|\Illl|U .IIHt`l'I\I`, h IIIIQ IIIH '~.I\ H s \I`.l\.i \| .'\|HIHI\|
f ~.\ 1|1i||/.u|n uu lmlw. IlI'_.|I'(`?.
|IIII
llI\| (lu 7t`IH, |HH|`lI\\'- \|N`H.IF IIII`)',I;II .\||I\' Igwz |\I' |.| \'\| I; m |'n|i |||| |
1\' \\7-
| \ '\\ ,~"\'.
. ` \\ .
-';;n|u|.| |_.\ u||-.|=.l\ mu ||f.|.n;n num IIIIFHI |n|| 1 \||. K '|. H mv; .I |. 1
'`|1| v\ \|1`
|m|.|, qm \.| = m.|||;_.|| | || u;||;|I| \~-.,|.|., .|. .W `|. ,.. MH
n.\ | .1 IN |\|\um.| .I|)||\.I\`.I\ nu llnn ,||, m || ||. |u~ \|\||n|| II mm In
/\m||'||. .m 11-'.u|||.\|,;\ ;| vlu. mm|\1||u~.\|~\~<I\-~
| I\HH|I ... |(,|.~.,`|.,
lI|H H|l|'|.||_ |III| !`||z^|H.I HIILI IIII.I)'t`III Hu /\I\|||\|
/res/layout/activity_main.xml
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="ll_parent" android:layout_height="ll_parent"
android:orientation="vertical" android:padding="16dp">
<ImageButton android:id="@+id/btAbrirCamera"
android:layout_width="60dp" android:layout_height="60dp"
android:src="@android:drawable/ic_menu_camera" android:tet="Abrir a Camera" />
<ImageView android:id="@+id/imagem" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_weight="1"
android:layout_gravity="center" />
Veja que o boto que vai disparar a intent est 'congurado com a imagem
android:src="@android:drawable/ic_menu_camera", nativa do Android. A seguir, temos o
cdigo da activity que dispara a intent para abrir a cmera:
MainActivity.java
AndroidManifest.xmI
<manifest . . .>
<uses-permission android:nane="android.permission.CAMERA" />
<application . . . />
Como j estudamos o que uma intent, acredito que este cdigo seja tranquilo para
voc. Ao disparar a intent, a cmera vai abrir, e depois de tirar a foto o resultado
ser entregue no mtodo onActivityResult(. . .). Como eu disse antes, cada aplicao
retorna s informaes de uma forma diferente. No caso da aplicao da cmera, o
retorno um objeto do tipo Bitmap. Para obter o Bitmap, devemos ler o parmetro
"data" do Bundle. Esse Bitmap mostrado no Imageview que est no layout.
A figura 21.7 mostra o resultado da foto. A primeira parte o layout antes de
tirar a foto, a segunda parte a aplicao da cmera executando no emulador e
a terceira parte a foto retornada pela cmera do emulador, que retorna sempre
a mesma gurinha apenas para simular.
?
1
MainActivity.java
import livroandroid.lib.utils.ImageResizeUtils;
import livroandroid.lib.utils.SDCardUtils;
public class MainActivity extends AppCompatActivity {
// Caminho para salvar o arquivo
private File le;
Google Android - 4' edio
});
if (savedInstanceState != null) {
// (*2) Se girou a tela, recupera o estado
le = (File) savedInstanceState.getSerializable("le")
showImage(le);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// (*3*) Salvar o estado caso gire a tela
outState.putSerializable("le", le);
}
@0verride
Como esse exemplo vai salvar um arquivo no SD card, preciso declarar a per
misso no arquivo de manifesto.
AndroidManifest.xmI
<manfest . . .>
<uses-permission androd:name="androd.permssion.CAMERA" />
<uses-permission androd:name="android.permission.NRITE_EXTERNAL_STORAGE" /
<app1caton . . . />
</manfest>
Mas as fotos tiradas com a cmera resultam em arquivos muito grandes, devido
alta resoluo das cmeras. Por exemplo, a foto de uma cmera pode ultrapassar
3.000 pixels de largura, mas muitas vezes o Imagevew que est no layout tem apenas
300 ou 500 pixels. Nesse caso, recomenda-se redimensionar a imagem original
para que ela que com um tamanho prximo do Imagevew.
justamente para facilitar o redimensionamento das fotos, utilizamos a classe
ImageReszeUti1s no exemplo anterior. Essa classe mostra como redimensionar a
foto da maneira correta, otimizando a memria do aplicativo. As explicaes deste
tpico visam fornecer a base terica, para que voc consiga entender o cdigo
-fonte dessa classe, que est disponvel no GitHub.
Para redimensionar uma gura, precisamos fazer quatro passos:
1. Obter as dimenses originais de largura e altura da figura.
2. Conhecer as novas dimenses para as quais a gura deve ser redimensionada.
3. Com base nestas dimenses , preciso calcular o fator de converso do
redimensionamento. Por exe
I mplo, se podemos diminuir a foto na metade,
ou em 1/4 do tamanho original.
4. Conhecendo o fator de converso, basta redimensionar a foto
de c ' ' - .
C3PtuIo 21 u Multimdia - udio, vdeo e cmera 611
NO P21550 1, podemos escolher qual o tamanho que a foto precisa ter. Mas o que
muitos aplicativos fazem ler o ta
manho que o Imageview utiliza no layout. O trecho
od1go a seguir mostra como implementar o passo 2, a m de extrair a largura
e altura da foto, sem carregar o arquivo inteiro em memria.
Uri uriFile = esta a Uri do arquivo da foto...
// Congura o BitmapFactor
y para apenas ler o tamanho da imagem (sem carreg-la em memria)
BitmapFactory.0ptions opts = new BitmapFactory.0ptions();
opts.inJustDecodeBounds = true;
// Faz o decode da imagem
BitmapFactory.decodeFile(uriFile.getPath(), opts);
// L a largura e altura do arquivo
int w = opts.outwidth; // largura original
int h = opts.outHeight // altura original
da imagem, respectivamente.
Agora vamos para o passo 3. Uma vez que o tamanho da gura original foi ob
tido, podemos calcular o atributo que vai denir a escala da foto, chamado de
inSampleSize. Esse clculo consiste em fazer uma simples diviso do tamanho total
da foto pelo tamanho desejado.
// Fator de escala
int scaleFactor = Math.min(w / width, h / height);
opts.inSampleSize = scaleFactor;
por ltimg vamos para o passo 4, que consiste em carregar o Bitmap em memria,
7
mas como o fator de escala foi denido, o tamanho da foto ser menor que a
origina] Ngre que para carregar a foto em memria o parmetro inJustDecodeBounds
deve ser false.
// Decode bitmap with inSampleSize Set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options);
612 Google Android - 4 edio
Entender esse exemplo fundamental para trabalhar com imagens no Android,
mente o tamanho e mem
pois mostramos uma foto em um Imagevew, utilizando so
ria necessrios. Note que a classe BtmapFactory contm v
rios mtodos para carregar
Btmaps em memria, como decodeByteArray(...), decodeF1e(-~-), d@Cd@R@5,U"C(. ~)
etc. Qual mtodo voc vai utilizar no importa. O que voc precisafazer e utlllzar
corretamente os truques com o atributo nJustDecodeBounds para primeiro obter O
tamanho original da gura, na sequncia calcular o fator de escala, e por ultimo
carregar a foto reduzida em memria.
Dica: para continuar seus estudos, recomendo uma leitura nos links adicionais
que esto no final do captulo. Dentre eles, importante ler 0 topico Loading Large
Btmaps Efcently da documentao ocial.
Como f z .
Intent i = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
i.putEtra(MediaStore.EXTRA_0UTPUT, Uri.fronFi1e(1e))'
startActivityForResu1t(i, 0);
Deixarei para voc concluir este exerccio. Caso ache necessrio, veja o projeto
VideoRecorder disponvel nos exemplos do livro.
Para gravar udio, tambm podemos utilizar uma intent, porm no caso do udio
o arquivo sempre salvo no diretrio interno do Android, portanto no preci
samos definir o arquivo com a localizao. O trecho de cdigo a seguir mostra
como disparar a intent para gravar um udio:
Intent i = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
startActivityForResu1t(i, G);
O resultado mais uma vez entregue no mtodo onActivityResuIt(); basta ler
o mtodo getData() da intent de retorno. O objeto Uri contm a localizao do
arquivo de udio, e conforme j aprendemos esse udio pode ser tocado com a
classe MediaPiayer.
http://developerandroid.com/training/managing-audio/iHCCX.html
http://developerandroid.com/training/camera/photobasics.html
Android Training - Recording Videos Simply
http://developer:android.com/trainmg/camera/videobasics. html
Android Training - Controlling the Camera
http://developer:android.com/training/camera/cameradirect.html
Android Training - Loading Large Bitmaps Efciently
Mapas
\,__d
\
'\
22.1 Introduo
Existem duas verses da API de mapas, que chamaremos de verses 1 e 2, ou apenas
V1 e V2. Em dezembro de 2012, o Google descontinuou a Vl. Portanto, neste livro,
vamos estudar a V2. Contudo, caso seja a primeira vez que voc esteja estudando
a API de mapas, explicaremos alguns conceitos bsicos da API antiga para voc
saber identicar o cdigo deprecated (obsoleto) quando o vir.
A primeira verso da API de mapas do Android era representada pela classe
Mapview, uma subclasse de View.
<LnearLayout . . . >
<com.googIe.androd.maps.Mapvew . . . />
</LnearLayout>
Contudo a API V1 e a classe Mapvew foram descontinuadas pelo Google. Dito isso,
1
o objetivo dessa breve explicao foi apenas informar o que voc no deve fazer,
pois normal que, alm deste livro, voc procure algum material auxiliar para
complementar os seus estudos. Ento, caso voc encontre um artigo na internet
ber
sobre a classe Mapvew, sa que ela foi descontinuada.
615
616 Google Android - 4= edio
22.2 Google Maps Android API - Verso 2
a nova API de mapas, chamada Google
Em dezembro de 2012, 0 Google lanou 0 0 V1 T d
Maps Android API V2, a qual apresenta muito mais funcionalidades que 21 - O O 0
framework de mapas foi criado utilizando vetores para suportar 35 VlSU3l1Z305
2D e 3D, o que tambm possibilita um ganho significativo no deserriP1h0 tor:
nando todas as interaes e animaes muito mais fluidas. A v1sual1zaao.3D e
feita automaticamente se o nvel de zoom estiver perto O b2lSE21Hf C Se na Cldade
em questo houver imagens 3D.
A API tambm ganhou diversas melhorias, mas a principal foi a utilizao de
fragments. Antigamente, com a V1, s era possvel exibir um mapa de cada vez
na tela, o que, no caso dos tablets, trouxe um grande problema. Mas como a V2
implementada com fragments, essa limitao no existe mais. Na prtica, trocamos
a classe Mapvew por MapFragment. Caso esteja utilizando a biblioteca de compatibi
lidade, que o nosso caso no aplicativo dos carros, use a classe SupportMapFragrnent.
app/buiId.gradIe
Compile 'com.google.androd.gms:play-services:7.0.0'
Caso o Android Studio informe que existe uma verso mais nova da biblioteca
quando voc for estudar o livro, que vontade para utilizar sempre as novas verses
Captulo 22 1 Mapas 617
N:z.
Sc (081 Sfa Clrlando ou movendo algumas APIs para o Google Play
O l_ '_ Vmagm que ml1'l0f18S Ou bugs podem ser corrigidos atualizando
ap icativo o Google Play Services pela loja.
Se for a primeira vez que estiver abrindo essa pgina com sua conta de email,
voc ver uma tela com um boto Create project, utilizado para criar um novo pro
jeto. No formulrio de criao do projeto, basta digitar um nome qualquer para
identic-lo. Eu recomendo que voc crie um projeto chamado Livro Android, assim
todas as conguraes que zermos referente ao livro caro nele.
Ao criar o projeto, note que o campo ProjectlD preenchido automaticamente, e
voc no precisa se preocupar com ele.
Logo depois de criar a congurao do projeto, voc ver uma pgina com algu
mas opes no menu lateral, como: Overview, APls&auth, Monitoring, Compute, Networking,
Storage, Big Data etc.
os de fazer na pgina do console habilitar o
A primeira congurao qL1 tm
Servio dos mapas. Para isso, acesse a pgina APls&auth > APIs e habilite o servio Google
Maps Andmd Apm Confgrme a figura 22.1. A figura mostra minha conta de testes. Veja
que 0 servio Google Cloud Messaging for Android para enviar mensagens por push tambm
est habilitado, mas vamo s estudar esse assunto em outro captulo.
618 Google Android - 4 edio
- U
"`) Google Developers V, _
(- C fi H; iu consoledevelopers.qoogletom, ,_
zW
' g., ,,-, ""` 'l' " == Y
1 ze rzgs ~ ' lirov;fi'oid@gIW'l CCV
|\`9(_`,\ \.'
Enabled APM-2
Oi.sei~.|e. '
Livro Android
. ,-~~
gt ,` , _
El? ng S. Settings u
Peri os
Apis Si null! u
._..,
O prximo passo criar a chave de acesso para o servio do Google Maps. Para
isso, precisamos obter o SHA-1 ngerprnt do certicado que voc utiliza para compilar
o projeto. Por padro, um certicado de debug criado automaticamente pelo
Android Studio, o qual ca na pasta do usurio do sistema operacional. A seguir,
temos a localizao deste arquivo no Windows e Linux:
Windows - C:\Users\usuario\android\debug./eeystore
Linux - /home/usuario/.android/debug.keystore
SHA
debugleeystore localizado na pasta do usurio. z
O resultado do
comando mostra vrias informaes sobre o certicado. Mas a
linha pela qual estamos interessados esta que exibe o SHA1ngerprint:
SHA1: C1:66:56:93:DF:DE:0B:B9:DC:ED:76:D7:65:B7:10:DC:1F:3F:0D:41
Nota: o comando keytool um executvel que est localizado na pasta bin do]DK.
API requests are sent directly to Google from your client Android device. Google verifies that each
Learn more
request originates from an Android application that matches one of the certificate SHA1 fingerprints f
using the following command: g
and package names listed below. You can discover the SHA1 fingerprint of your developer certificate ;
I
One SHA1 certificate fingerprint and package name pafaled by 3 5em'|) Pef line- EamPle- i
45B5:E4:6F'361AD'0A:98:94:B4:02:66r2B:12:17:F2'56:26.AOIEO;c0m.E'mPl`
C1'66'56'Q3'DF'DE'0B`B9ZDCZEDI7ID75ZB7Z1OZDCZTFZ3FIODI41;bf.COfTl.lVfO3I1dfOd.ClT0 i
i
Autmoio c1zzszoa:tF:oE:oa~B9:oc:Etz7:t7:5;B7z1o~oc1Fz3F:ot:41;br.0mzlM2dfd-2ffS
APPLICATIONS
Nota: uma chave de acesso utilizada por uma aplicao para autenticar o
acesso nos servios do Google. Copie a API Key, pois vamos utiliz-la depois para
congurar o arquivo de manifesto do projeto.
app/buiId.gradle
A1O
compile 'com.googIe.android.gms:play-services:7.0.0'
AndroidManifest.xml
<?mI version="1.0" encodng="utf-8"?>
<manifest ... package="br.com.Iivroandroid.carros"
U5@S'D@FWSS0n androd:name="android.permisson.INTERNET" />
Captulo 22 I Mapas
621
uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /
<meta-data
android:name="com.google.android.gns.version"
android:value:"@integer/google_play_services_version" /
meta-data android:name="com.google.android.naps.v2.API_KEY"
android:value:"@string/API_KEY"/>
<activity . . . />
/res/vaIues/strings_cong.xmI
<zm1 Vef50n:"1,O" encoding="utf-8"?>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /
Nota: mesmo que voc no utilize a API de localizao no cdigo, caso a opo my
location esteja habilitada no mapa pela API, as permisses de GPS so necessrias.
http://de velopcn and roid. com/a bout/dush boa rds/i ndex. html # Open G L
<string name="API_KEY">AIzaSyDYCu8-Ijlg-dug-VzT1SC_6fekDKn@oTQ</strng>
Lembre-se de que cada chave diferente e voc precisa utilizar a sua, caso contrrio
o mapa no vai funcionar. Isso porque cada chave gerada baseada no certicado
digital utilizado no desenvolvimento, e naturalmente o arquivo debug. keystore que
est na sua mquina diferente do meu.
Uma dica interessante no caso de empresas nas quais existem vrios desenvolvedo
res no time utilizar o mesmo arquivo debuglzeystore em todos os computadores.
Assim a chave para todos a mesma, ento voc poder instalaro aplicativoide seu
. ' ` ' brando ue todas as
computador, ou do computador de um colega de trabalho, pois todos utilizam o
mfzgmg arquivo debugkeystore para assinar o aplicativo. Lem q U '
. ~ - ' ' ' e uma a lica ao assinada com o
apligagg, no Android precisam ser assinadas por um certicado e, caso Ja exista
uma aphcaao instalada no dispositivo, soment p
mesmo certificado pode atualiz-la.
z4
_ .--ra a p
22.6 Adicionando o mapa no projeto d0S Cff$
t s todo o ro eto vamosarte pa
f ~ ro. . que
mais fcil
Depois que conguramo P J d arms existem Os
mostrar a fbrica do carro no mapa. No XML OU JSN 05 C '
Google Android - 4= edio
Por exemplo, o carro Ferrari FF est com a localizao denida para a fbrica da
Ferrari, em algum lugar da Itlia. Nosso objetivo mostrar uma activity com um
mapa posicionado na localizao da fbrica do carro. Faremos isso ao clicar no
boto Mapa que j existe na tela de detalhes do carro.
O passo a passo para mostrar um mapa ser parecido com o que zemos para
mostrar um vdeo. No caso do vdeo, criamos as classes VideoActivity para controlar
a navegao, e criamos a classe VideoFragment para gerenciar 0 contedo e O layout.
Dentro do VideoFragnent utilizamos o Videoview. Desta vez, vamos criar as classes
MapaActivity e MapaFragnent. Dentro da classe MapaFragment, vamos inserir 0 componente
de mapas da API V2, que o fragment SupportMapFragment. Ento mos obra! Abra o
projeto dos carros novamente e crie uma activity conforme demonstrado a seguir.
Ela recebe o objeto carro por parmetro e delega o trabalho para um fragment.
MapaActivity.java
package br.com.livroandroid.carros.activity;
import br.con.livroandroid.carros.fragments.HapaFragment;
public class MapaActivity extends BaseActivity {
private Carro carro;
@0verride
getSupportActionBar().setDisplayHomeAsUpEnabled(true)
if (savedInstanceState == null) { ,
Captulo 22 : Mapas
625
mapaFragment).commit(); . id 'fragLayout'
agmentManager()'be91"Tra"5aCt0() FDlace(R
}
@0verride
/res/layout/activity_mapa.mI
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="match_parent">
<include layout:"@layout/include_toolbar" />
<FrameLayout android:id="@+id/fragLayout"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:layout:"@layout/fragment_mapa" />
AndroidManifest.xmI
<application . . .>
MapaFragment.java
package br.com.livroandroid.carros.fragments;
import com.google.android.gms.maps.SupportMapFragment;
public class MapaFragment extends BaseFragment implements 0nMapReadyCallback {
// Objeto que controla o Google Maps
private Googleap map;
private Carro carro;
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_mapa, container, false);
// Recupera o fragment que est no layout
// Utiliza o getChildFragmentManager() pois um fragment dentro do outro
SupportMapFragment mapFragment = (SupportMapFragment)
getChildFragmentManager().ndFragmentById(R.id.mapFragment);
// Inicia o Google Maps dentro do fragment
mapFragment.getMapAsync(this);
this.carro = (Carro) getArguments().getSerializable("carro")
return view;
}
@Override
public void onMapReady(GoogleMap map) {
// O mtodo onMapReady(map) chamado quando a inicializao do mapa estiver Ok.
this.map = map;
if(carro != null) {
// Ativa o boto para mostrar minha localizao
map.setMyLocationEnabled(true);
// Cria o objeto LatLng com a coordenada da fbrica
LatLng location = new LatLng(Double.parseDouble(carro.latitude),
Double.parseDouble(carro.longitude));
// Posiciona o mapa na coordenada da fbrica (zoom = 13)
CameraUpdate update =~CameraUpdateFactory.newLatLngZoom(location, 13);
map.moveCamera(update);
// Marcador no local da fbrica
map.addMarker(new MarkerOptions()
.title(carro.nome)
.snippet(carro.desc)
.position(location));
// Tipo do mapa:
// (normal, satlite, terreno ou hibrido)
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
}
}
Itnhamos
. . .``'.
Captulo 22 - Mapas 627
entro
dmP0I't1nte. como o fragment da biblioteca de mapas SupportMapFragnent est
O nosso fragment MapaFragnent, temos uma situaao que ainda nao
f visto no livro, que e um fragment dentro de outro. Por isso, para obter
o ragment SupportMapFragnent foi utilizado o mtodo getChildFragmentManager() em
Vez de 9etFragnentManager().
/res/layout/fragment_vdeo.xmI
<FraneLayout . . . >
<fragment android:id="@+id/mapFragment"
class="con.google.android.gns.maps.SupportMapFragnent"
android:layout_width="match_parent" android:layout_height="natch_parent"
/>
Para nalizar o exemplo, volte classe CarroFragment, que a tela com os detalhes
do carro que tem os botes Vdeo e Mapa na action bar. Altere o trecho de cdigo
do PopupMenu cujo alerta inamos com as opes. Na opo que mostra o mapa,
vamos navegar para a activity que acabamos de criar.
CarroFragment.java
public class CarroFragnent extends BaseFragnent {
popupMenu.set0nMenuItemClickListener(new PopupMenu.OnMenuItenClickListener() {
@Override
public boolean onMenuItemClick(MenuIten item) {
,. -, -. `-aiais
osicionar -mapa
API.
que mostra informaoes sobre essa localizaao
detalhes da
my location habilitado, que ao ser clicado v P
usurio. Nos topicos a segu1f,V2m05 estudar m
Qjzfii
* r _ _ . J (_
. W; .. `V ' j.;\, _ ,
@
I
._FGT
, -r-- '` Ff
QITO ' . O"`
1
vmar 5" 'F _ ffnzwtil
PPI"
klnnlori-
www
T
U.ll'Q-ID
_
vb (W nz o~ OT'
~'.ruv.5 .
r1O\lr
alterar o t1 o d ' ~
ez que temos o objeto GoogleMap em mos,
_ m O mapa, como alterar sua posiao, adicionar marcadores,
Cdi f P d ualizaao, fazer desenhos etc. Para revisar os conceitos, veja o
go- onte a classe MapaFragment que zemos_
@Override
630 Google Android - 4 edio
public void onCancel() {
Toast.makeTet(getContet(), "Animao cancelada.", Toast.LENGTH_SHORT).show();
}
});
Note que, para alterar a posio e o zoom da cmera, necessrio criar primeiramente
um objeto CameraPosition, o qual pode facilmente ser criado pelo CameraPosition .Bui1der,
que, como o prprio nome indica, uma classe de criao de objetos que segue o
padro Builder do GoF. Depois que o objeto CameraPosition for criado, necessrio
criar o CameraUpdate baseado nele, e somente depois disso atualizam-se essas infor
maes no mapa com os mtodos moveCamera(update) ou animateCamera( update).
Resumindo, este trecho de cdigo que utilizamos anteriormente:
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(1atLng, 15); ]/ 15 o zoom
Mas a classe CameraUpdateFactory tem vrios atalhos. Por exemplo, se voc precisa
configurar apenas o zoom, basta utilizar o seguinte cdigo:
CameraUpdate update = CameraUpdateFactory.zoomTo(10);
Captulo 22 1 Mapas 631
A vantagem de utiliza
r a classe CameraUpdateFactory para atualizar o zoom que ela
vai manter
I _a localizao
_ apenas atual e o zoom. Outra forma de controlar
alterar
o zoom e utilizar os seguintes mtodos'
CameraUpdateFactory.zoomIn()
CameraUpdateFactory.zoom0ut()
CameraUpdateFactory.zoomTo(zoom)
CameraUpdateFactory.zoomBy(zoom)
GO0glM3p . MAP_TYPE_NONE
Goog1eMap.MAP_TYPE_NORMAL
GoogleMap.MAP_TYPE_SATELLITE
GoogleMap.MAP_TYPE_HYBRID
Googleap.MAP_TYPE_TERRAIN
Gi /res/menu/menu_frag_mapa.xmI
<menu mlns:androd="http://schemas.androd.com/apk/res/androd"
mlns:app="http://schemas.android.com/apk/res-auto">
<tem
android:id:"@+id/action_locaton_carro"
android:ttle="@strng/mapa_locaton_carro"
androd:icon:"@drawable/c_acton_place"
app:showAsActon="always" />
<tem
androd:id:"@+d/acton_locaton_directions"
androld:ttle="@string/mapa_locaton_directions"
android:icon:"@drawable/lc_acton_drectons"
app:showAsActon="always" />
<tEm
androd:id:"@+id/action_mapa_normal"
androd:title:"@strng/mapa_normal"
app:showAsActon="never" /
t9
androd;id:"@+td/actlon_mapa_satelite"
androld:title="@strng/mapa_satelte"
app:showAsActon="never" /
Captulo 22 1 Mapas
633
<item
a"drd3d="@+d/action_mapa_terreno"
androld:title:"@5tf"9/WD_terreno"
app:showAsAction="never" />
<tem
<item
android:id="@+id/action_zoom_in"
android:title:"@string/mapa_zoom_in"
app:showAsAction="never" />
<item
android:id="@+id/action_zoom_out"
android:title:"@string/mapa_zoom_out"
app:showAsActon="never" />
Esse arquivo de menu contm opes para alterar o modo de visualizao do mapa
(normal, satlite, terreno e hbrido) e aes para aumentar ou diminuir o zoom.
Tambm adicionei uma ao para mostrar a localizao da fbrica do carro e outra
para mostrar uma rota da localizao atual at a localizao da fbrica do carro.
Para o cdigo compilar, crie estas strings:
/res/values/strings.xmI
<?m1 version="1.0" encoding="utf-8"?>
Feito isso, basta inflarmos esse menu para mostrar os itens na action bar. O cdigo
est comentado, portanto leia com ateno. Lembre-se de que para um fragment
adicionar aes na action bar ele precisa chamar o mtodo setHas0ptionsMenu( true).
MapaFragment.java
return view;
}
@0verride
public void onCreate0ptionsHenu(Henu menu, Henulnater inater) {
super.onCreate0ptionsMenu(menu, inater);
inater.inate(R.menu.menu_frag_mapa, menu);
}
@Override
public boolean on0ptionsItemSelected(HenuItem item) {
if(map != null && carro != null) {
if (item.getItemId() == R.id.action_location_carro) {
// Posiciona mapa na localizao da fbrica
LatLng location = new LatLng(Double parSeDOUble(carr0.latitude),
Double.parseDouble(carro.longitude));
map.animateCamera(CameraUpdateFactory.newLatLngZoom(loc ation, 13));
} else if (item.getItemId() == R.id.action_location_directions) {
// Posiciona mapa no usurio
toast("Mostrar rota/direes at a fbrica.");
} else if (item.getItemId() == R.id.action_zoom_in) {
toast("zoom +");
map.animateCamera(CameraUpdateFactory.zoomIn());
} else if (item.getItemId() == R.id.action_zoom_out) {
toast("zoom -");
map.animateamera(CameraUpdateFactory.zoomOut());
} else if (item.getItemId() == R.id.action_mapa_normal) {
// Modo Normal
'map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
} else if (item.getItemId() == R.id.action_mapa_satelite) {
Captulo 22 n Mapas 635
// Modo Satlite
map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
} else if (tem.getItemId() == R.id.acton_mapa_terreno) {
// Modo Terreno
map.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
} else if (item.getItemId() == R.td.action_mapa_htbrtdo) {
// Modo Hibrido
map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
}
Satlite, Terreno e Hbrido. Tambm adicionei os botes Zoom (+) e Zoom (-), que
brincam com o zoom do mapa, basta voc testar. Todas essas opes ficam no
menu overflow da action bar. Como action buttons deixei apenas o boto que
vai posicionar o mapa na coordenada do carro e o boto para traar uma rota
(falaremos da rota posteriormente).
Q 4 Q 205
un um.V ) .,
"-;7=I' L 43..?7"* J ";5*'7mz-
.M _;/1
~ za ' '
-.v-~`.----.ae-z-z~ ?*'=?'i' - .* '5~.~ `3, 333
,,z-__,~;,,,:~;._,@z.=:z;z,:z
9` ' ;1.',W:-
zu
_= V
m.z:.*~
1:1 2
CEI rw,
vlr
Yzcaat.
i
fz>.~z=
Em
wa . fr
>'A z
I _ mv mf ms ...uns
,~z _
'gzuzeams
. V t __ msvv 2 V
az - * . `
22.12 CameraPosition
Conforme j estudamos, 21 classe CameraPoston representa a posio do mapa e,
' " bm o zoom. Igualmen
nela, podemos configurar tanto a localizaao como tam
te vinnos q_ ue a classe utilitria CameraUpdateFactory pode facilitar o trabalho, pois
636 Google Android - 4' edio
realiza dois passos ein um s. Contudo, a classe CameraPosition faz muito mais do
que apenas informar a localizao e o zoom, sendo assim, vamos explorar alguns
mtodos importantes como o bearing(graus) e tilt(graus).
Vamos continuar os estudos sobre a API de mapas, mas faremos isso fora do pro
jeto dos carros. Para acompanhar as prximas explicaes, abra o projeto deste
captulo Demo-MapsV2 no Android Studio. O trecho de cdigo a seguir demonstra os
mtodos bearing(graus) e tilt(graus), que tambm podemos informar para turbinar
a visualizao do mapa:
Googleap map = ...;
// Localizao do mapa (Av. Paulista - SP)
LatLng latLng = new LatLng(-23.564224,-46.653156);
nal CameraPosition position = new CameraPosition.Builder()
.target(latLng) // Localizao
.bearing(G) // Rotao da cmera em graus
.tilt(0) // ngulo em que a cmera est posicionada em graus
.zoom(15) // Zoom
.build();
Cameraupdate update = CameraUpdateFactory.newCameraPosition(position);
Note que a linguagem java permite quebrar a linha depois do ponto, para chamar
mltiplos metodos em sequncia, mas isso tambm pode ser escrito em uma
nica linha de cdigo, conforme a sua preferncia:
nal CameraPosition position =
new CameraPosition.Builder().target(latLng).bearing(0).tilt(0).zoom(15).build();
Camerapdate update = CameraUpdateFactory.newCameraPosition(position);
Nota: a palavra cmera pode gerar alguma confuso caso voc a associe com
uma cmera que tira fotos. Na verdade, o termo cmera utilizado porque a
visualizao do mapa funciona exatamente como se estivssemos enxergando
o mundo com uma cmera em um plano linear. Por pad ro, a cmera exibe os
mapas de cima para baixo, mas podemos alterar esse ngulo de visualizao,
rotao da cmera etc.
. .I.,, ~I.
que faz a propriedade bearing da classe CameraPosti.on.
`x
` Map Map
7_
4+A+ . . /
'
fE
fe
Nota: para rotacionar o mapa manualmente pelo touch, utilize dois dedos e faa
um crculo como se estivesse girando uma moeda. Na prtica, isso o mesmo
que alterar a propriedade bearing.
\I $\
:kar
z .. '7Q
`^
,,S\ e'
G*
,
_`.,
z-. [I]
_\<_.+.-f`
,ktKM
'V\:\ xx ,._.
.f
\`N,
fr
\_
638 Google Android - 4 edio
Mapa vz E M,,,a-V :___ Mapa \/,_
.f_ _ A,
_ ir .
L 3
N
X,
vn
.
r
_ V
`
N `1\~_j
l"
v;, G ~._._\(z] rf z z Q] V
1.
.m3 <'l
_I Wm
;Mapa-V2
E! 9Mapaq/2
\ sMapa-V2
9 ' dia 9
< 96
Hgzmz 22.7 - Puinzctro til! (/im'1na) com os Lulfws O, 45 c QO.
E:.>'nI' (('Q1} me /`\'\(1'(i( AFN ` Lxeinpin Google Maps f\r\(`"("'( API vf' i'x>m,:z~ "
, af.
U
L]
.~ `i \ \ \\\ _
f N \
_ .` _ , .,_ U ir, zw
1'I`Ill122.8 - z\f1.1m um 51) mm H/1 (1H1HIg zli' mm >> 1.1/z_~> . 4.1 U .
22.16 Marcadores
// Adiciona um marcador
private void adicionarMarcador(Goog1eMap map, LatLng latLng) {
Markerptions marker0ptions = new Marker0ptions();
marker0ptions.position(latLng).title("Meu Marcador").snippet("Livro Android");
Marker marker = map.addMarker(marker0ptions);
l
Para adicionar um marcador, utilizamos um objeto do tipo Marker0ptions, e para con
gurar a localizao do marcador, o mtodo position(LatLng). O retorno do mtodo
Goog1eMap addMarker(marker0ptions) o objeto Marker, que vai conter as propriedades que
lhe fram atribudas Por padro, o marcador tem o cone do Google Maps, mas caso
seja necessrio alterar o cone chame o mtodo Markeroption.icon(BitmapDescriptor):
// Adiciona um marcador 1
MarkerOptions H _ |_, _ H
private void adicionarMarcador(GooglMP W601 |-HU-U9 fl-HQ) f
markeroptong z new Marker0ptions();
marker0ptions position(1atLng).tit1e("Meu Marcador ).snippet( Livro Android );
markeroptong icon(BitmapDescriptorFactory_fromResource(R.mipmap.ic_launcher));
Marker marker z map.addMarker(8Fk@F0Pt0S)J
}
640 Google Android - 4 edio
Ao clicar no marcador, uma janela exibida com os textos denidos nas proprieda
des title e snippet. A gura 22.9 mostra essa janela, que chamada de info window. Para
customizar a janela infowindow, utilize o mtodo GoogleMap.setInfowindowAdapter( adapter)
passando como parmetro um objeto que implemente a interface InfoHindowAdapter.
Essa interface contm dois mtodos: getInfowindow() e getInfoContents(). O mtodo
getInfowindow() deve retornar a view que a borda da janela, alm de todo o con
tedo interno. Mas caso voc deseje alterar apenas o contedo interno da janela,
como ttulo e descrio, utilize o mtodo getInfoContents().
1
i Exemplo ~ Google Mao: Android API v2 2
Meu Marcador
1~ j V ` ` A `1
AI fan
R Pamplona ~ Y~=z.f-- wzz
AI Riofflaro
E2
T -in :masa m
ft l ., u
Figura 22.9 - janela-padro ao clicar em um marcador
});
}
A gura 22.10 demonstra o resultado desse cdigo, cuja janela interna, que exi
. - ' dada
3
= - 15 1
{ Exemplo ~ Google Maps Android API v2
_
rf Q 1% 1;-vz .f;=.,;'-3 os- ^ E
lt `
1 t.
l_;
, fx
' .
H?Livro
IIx *
:rw
~-~
1`' __;\.._l
ti' *f 1
- _V'
View
1 '.lz:Android
;:'
l*_ lt2
customlzada
, l ~. _3Al,Campinas
z . 1 1 p . _ A ,
H;-_;
H-
" .l'
:ff ~,' :z:- ~ . G Ff ' ga |
M,* 1
` \ / ~ * ff 52 'z
fz
` ,\ fl*
; 1 3 |;
z`;,L|
| R Pamplona 1 wo lvvbl 1 ff 3
l
; iti 1,,,
ll ~
V.` ea
. V
" eL
Al Rio Claro 1
1 {=<~-fvll + `
lL__,___
Gong'__"___,
_.-_oo..o
__ _ _ ,._
l, _.M
i1 .fya.
*,-
rz
zw ~.z' l__
za*
.mz
1 ~'*_,. '. t
.
<-
, Exemplo Google Maps androod API v2
\_1, :1_',4, .
I 'H V|ew:ustom|zada~~
. .
E
l Met;f.~lu~ra:,:
' Livro Android
.. , ` H ~,,,, _.
QfVll2'229,i
z
_
,
z V"~=
' R Pamplona ~~<z.[]
Al H|o'L,laro` .:~
Lnnon +
_ 1
p (.03lt* 41,3 ' _ _
Figura 22.11 - janela com a borda customizado.
retornarnu
..Q
Captulo 22 I Mapas 643
uecom' `leta~b ' . ' - . ,
Nota: o mtodo getInfoCon
j aprendemos a customiz
ar a aparncia da janela de detalhes do marcador ue
chamada de Info wlndow. Agora vamos aprender a monitorar o clique nesse marcador.
Para isso, basta utilizar o mtodo setOnMarkerClickListener(listener):
// Evento ao clicar no marcador
7
map.setOnMarkerClickListener(new 0nMarkerClickListener() {
@Override
});
});
_ , _ , - 'ma-em
- ~ortanto
' ' Paravoc no
tratar o
- ,- ~' ko1sCl'
naokListener(listener)
teria efeito algum.
Nota: a janela info window e renderizada como uma 1 g P
P --
ode adicionar botoes a essa view P
evento de Cl1qL1 116553set0nMar
- ' lize o metodo er Aic_ M W , _ ._
J2n1a HU
644 Google Android - 4' edio
22.17 Polyline -desenhando uma linha no mapa
A classe Poiyiine permite desenhar uma linha no mapa, que, na verdade, uma
sequncia de coordenadas.
No exemplo anterior, criamos o mtodo adicionarharcador(Googlehap,LatLng), 0 qual
adiciona um marcador localizao desejada. Podemos chamar esse mtodo
quantas vezes forem necessrias para adicionar vrios marcadores. Por exemplo,
o seguinte trecho de cdigo adiciona dois marcadores:
LatLng iatLng = new LatLng(-23.564224, -46.653156);
LatLng 1atLng2 = new LatLng(-23.555696, -46.662627);
I/ Adiciona os marcadores
adicionarMarcador(map, 1atLng);
adicionarMarcador(map, 1atLng2);
A gura 22.12 exibe o resultado desse trecho de cdigo com uma linha entre os
dois marcadores. Apenas para brincar novamente com 0 conceito de inclinao
da cmera do mapa, a gura da esquerda est com a propriedade tilt = 0 e da
direita com o tiit = 90. Note que, embora o zoom da gura da direita seja maior
(mais prximo), o parmetro tilt permite inclinar a cmera, aumentando 0 ho
rizonte de visualizao.
Captulo 22 1 Mapas
Vista`auhsta
I,
4 Exem Io Goo IeM
Exemplo - Goo 9|& Maps Android API v2
AI Joaquim Eugnio de Lima p Q 3p$ Al'ldf0ld API v2
.1_ P I
tuna
El R Pampl
Qfv Al Rio Cl
R\m\>"Campos
uW
rnM/
(9 Al M R h A
3
fi F C H P J M
H^91 f
[B C K + i
I E1
n z I :nun ]
<
na c
,
Av,Ang|ca
az zv I 49 a on /R
oogle A isbauas GOQ 4%
Figura 22.12 Dozs marcadores e uma lmha
23.1 Introduo
j sabemos que o Google Play Services um aplicativo distribudo pelo Google
Play que apresenta funcionalidades essenciais para se comunicar com os servios
do Google, alm de vrias APIs.
At o momento, vimos que a API de mapas V2 faz parte do Google Play Services.
porm na prtica no foi necessrio se conectar aos servidores do Google. Neste
captulo, faremos essa conexo para obter a localizao por GPS. Lembrando que
naturalmente o Google Play Services precisa estar declarado como dependncia
do projeto.
app/buld.gradIe
Compile 'com.google.android.gms:play-services:7.0.0'
646
U''-.
CPUO 23 I Google Play Services e localizao 647
23.2 My Location
Essa bolinha azul chamada de My Location, e acredito que, se voc utiliza o aplica
tivo do Google Maps, sabe do que estou falando.
Porem, se voce deseja obter as coordenadas, e no apenas mostrar a localizao
do usuario no mapa, necessrio ligar o monitoramento de GPS.
@Override
protected void onPause() i
super.onPause(); // DESQH 0 GP5
locationManager.removeUpdates(h5)5
}
648
Feito isso, sempre que o Android tiver uma nova localizao, ele chamar
o mtodo onLocationChanged(location), passando como parmetro um objeto
and roid . location . Location.
@0verride
public void onLocationChanged(Location location) {
// Faa algo com esta coordenada aqui
double latitude = location.getLatitude();
double longitude = location.getLongitude();
}
Basicamente isso. Como vimos, simples. O mais importante que voc precisa
saber de tudo isso o conceito de provedor de GPS (GPS Provider). O mtodo
requestLocationUpdates( . . .) recebe como parmetro o provedor de GPS, que pode ser
uma das duas constantes LocationManager _ GPS_PROVIDER ou Locationanager.NETw0RK_PROVIDER.
caso ~ - - .
1 af PIHO, qual provedor utilizar? O provedor GPS_PROVIDER preciso, porem e
en o]aeouti
-provedor NETw0RK_PROVIDER
izar a API antiga, era: Utilize no preciso, porm rpido. A resposta, no
os dois!
// Liga o GPS
LocationManager 1ocationManager z _ _ _;
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, G), this);
locationManager.requestLocationUpdates(LocationManager.NETwORK_PROVIDER, 0, 0, this);
@0verrde
protected void onStop() {
super.onStop();
mGoogleApClent.disconnect();
}
@0verride
Agora que sabemos como nos conectar aos servios do Google, vamos estudar a
API de localizao do Google Play Services.
/res/layout/activity_main.xml
_,_-u
<LinearLayout android:orientation-"vertical . . >
<TetView android:id="@+id/text" _ H
androd-iayout wdth="wrap_content" android:layout_height="wrap_content
androd-tet="Mostra como obter a ltima localizaao pela API."/>
<fragment android:id="@+d/Wap" . .. H
andr0d'1ay0Ut wdth="match_parent" android:layout_height= match_parent
class="com google.android.9ms.maD5-5UPPrtMaPFra9et" />
652 Google Android - 4 edio
Quando a aplicao abrir, o mapa ser mostrado localizao padro. Vamos
inserir um boto na action bar que, ao ser clicado, vai recuperar a ltima localiza
o conhecida e centralizar o mapa nesse local. Esse boto vai simular o mtodo
setMyLocationEnab1ed(boolean) da classe Goog1eMap.
@ /res/menu/activity_main.xml
<menu . . .>
<item android:id="@+id/action_my_1ocation"
android:tit1e="@string/action_my_1ocation"
android:icon:"@drawable/ic_action_1ocation"
app:showAsAction="a1ways" />
ManAttivity.java
public class MainActivity extends AppCompatActivity implements OnHapReadyCaI1back,
Goog1eApiC1ient.ConnectionCa11backs, Goog1eApiC1ient.OnConnectionFai1edListener {
private static nal String TAG = "1ivroandroid";
protected Goog1eMap map;
private SupportMapFragment mapFragment;
private Goog1eApiC1ient mGoog1eApiC1ient;
@0verride
protected void onCreate(Bund1e icicle) {
super.onCreate(icic1e);
setContentView(R.1ayout.activity_main);
// Fragment de mapa
mapFragment = (SupportMapFragment) getSupportFragmentManager().ndFragmentById(R.id.map);
mapFragment.getMapAsync(this);
Capt"| 23 ' G9|@ Play Services e localizao
@Override
} WHP.setMapType(GoogleMap.MAP_TYPE_NORMAL);
@Override
@Override
public void onConnectionSuspended(int cause) {
toast("Conexo interrompida.");
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
toast("Erro ao conectar: " + COHHGCOHRGSU);
}
@Override
public boolean onCreate0ptionsMenu(Menu menu) {
getMenuInater().inate(R.menu.menu_main, menu);
return super.onCreate0ptionsMenu(WGHU)5
}
654 Google Android - 4 edio
@Override
public boolean on0ptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_my_location:
// Obtm ltima localizao
Location l = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
Log.d(TAG, "lastLocation: " + l);
// Atualiza a localizao do mapa
setMapLocation(l);
return true;
}
return super.on0ptionsItemSelected(item);
}
'Y'
rigol de 50513
lc
R P\o\ a , ado
lgev H2\\' Ne
I'
lmulv
=,\%
<.o\>*oj
f>
'E
< . ol\; 3
a ton an e l . _
Por ltimo ar - ~
ODLOC t. Ch , pd a receber as localizaoes, devemos implementar o mtodo
9 ( PCHWW) da interface Locationustener. Pronto, isso tudo!
@Override
MainActivity.java
public class MainActivity implements 0nMapReadyCallback, GoogleApiClient.
ConnectionCallbacks, GoogleApiClient.0nConnectionFailedListener,
com.google.android.gns.location.LocationListener {
@Override
protected void onCreate(Bundle icicle) i - - ~}
public void onMapReady(G0091@MaP apl { ' ' '}
protected void onStart() { - - - l
}
@Override
protected void onSt0D() {
// Para o GPS
stopLocationUpdates( ); _
// Desconecta do G009l@ Play Serv1CS
mGoogleApiClient.disconnect();
super.onStop();
658 Google Android - 4 edio
}
@Override
public void onConnected(Bundle bundle) {
toast("Conectado ao Google Play Services!")'J
// Inicia o GPS
startLocationUpdates();
}
@0verride
public void onLocationChanged(Location location) {
Log.d(TAG,"onLocationChanged(): " + location);
// Nova localizao: atualiza a interface
setHapLocation(location);
}
Recomendo executar esse exemplo em um dispositivo real e fazer seus testes. Esse
cdigo atualiza a posio do mapa sempre que uma nova localizao recebida
do GPS. Note que a activity implementa a interface LocationListener e o mtodo
onLocationChanged() para receber as localizaes.
Nota:l _ - - , _
<aP*' 23 ' G9l Play Services e localizao 659
P oder fazer uma requisio HTTP neste Web service do Google, informando as
coordenadas de origem e ClSI1I101
// Descobrir a rota
String origem z "-25.443195, -49180977 ;
string destino = "-25.442207, -49-2784@3"1 _ _ _ __ _
String url ' llllttp'//Fips-g00Ql.D`l.S.COf'i/l'iDS/3D/d`iI`C`lOI`iS/]S0?0I`1g`Ll'i= + origem +
"&destination="+destino+"8zsensor=true8mode=driving";
Y
a \f`
f
' W av
al
lf`
\
~
l
( Ir H
. - nu
a
_
1 io? f
'O wifi
-\ _ _.z~.4
fp*,,...ei
.PW . '
I.'V)
K" "
w^'l|1 z
L .1 ~{-"'H
mnecnons usr )
Figura 23.2 - Desenhando uma rota.
if (tem.getItemId() == R.id.action_1ocaton_drections) {
// Poscona mapa no usurio
t0a5t("Mostrar rota/direes at a fbrica.");
}
List
Isso pode ser feito facilmente com a classe android.location.Geocoder e o m
todo getFronLocationNane(string), que retorna uma lista de objetos do tipo
android.location .Address, sendo que a classe Address contm a latitude e a longitude
do local encontrado. O retorno uma lista de possveis resultados, assim, geral
mente, o primeiro objeto o mais prximo do resultado:
// Para descobrir a latitude e a longitude do endereo
String endereco = "Av Paulista - SP";
Geocoder gc = new Geocoder(this, new Locale("pt","BR"));
https://developer android.com/training/location/index.html
Google Developers - Google Directions API
Este captulo fala sobre a classe BroadcastRecever, uma das mais importantes na
arquitetura do Android. Ela utilizada para que aplicaes possam interceptar
eventos de sistema, ou seja, mensagens geradas por uma intent.
Na prtica, o broadcast receiver uma classe que consegue interceptar uma intent,
sendo a intent uma mensagem que pode ser enviada pela sua prpria aplicao,
por uma aplicao de terceiros ou pelo prprio sistema operacional.
Um broadcast receiver sempre executa em segundo plano (background) e no
utiliza interface grca. Seu objetivo interceptar uma mensagem (intent) e
process-la sem que o usurio perceba.
24.1 Introduo
A classe androd.content.BroadcastRecever utilizada para interceptar mensa
gens enviadas por uma intent. Quando a mensagem interceptada, o mtodo
onReceve(context,ntent) chamado, e executa em segundo plano, sem mostrar
nenhuma tela ao usurio.
Um broadcast receiver pode ser utilizado para enviar e receber mensagens dentro
da prpria aplicao ou para interceptar eventos de sistema. Um exemplo de evento
de sistema a mensagem SMS_RECEIVED, a qual enviada pelo Android sempre que
uma nova mensagem SMS recebida pelo sistema operacional. Portanto, caso seja
interessante para sua aplicao, possvel interceptar essas mensagens, ou seja.
esses eventos, a m de executar alguma aplicao.
Um broadcast receiver consegue interceptar uma intent/mensagem mesmo se a
aplicao estiver parada, e esse um de seus grandes benefcios.
Captulo 24 n Broadcastkeceiver 663
Um rec ` , . . . .
24.2 Congurando um receiver de forma esttica
ou de f ' ^ ' f - . .
eiver pode ser configurado de forma estatica no arquivo Andro1dManzfest.xml
orma dinamica no codigo. Para comear a brincadeira, vamos aprender
a congur-lo de forma esttica. A r' ` 1
gura
d opdeincipa vantagem de um receiver ser con
forma estatica e que ele consegue interceptar o evento mesmo se a
aplicaao estiver fechada,
ou seja,` e1e consegue acordar a aplicao se necessrio.
Para estudarmos o conceito, crie um projeto chamado HeIIoReceiver. No arquivo de
layout da activity vamos apenas colocar um simples boto que vai enviar uma intent.
/res/layout/activity_main.xmI
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="16dp" android:orientation="vertical">
<Button android:id="@+id/btEnviar" android:tet="Enviar"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
No cdigo da activity vamos disparar uma intent por broadcast. Isso feito com
0 mtodo sendBroadcast(intent).
MainActivity.java
public class MainActivity extends AppCompatActivity inplements View.0nClickListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
ndViewById(R.id.btEnviar).setOnClickListener(this);
}
@Override
}dbd
public void onClick(View v) { _ _ _
// Dgpara a mensagem de broadcast para todas as aplicaoes instaladas
sendBroadcast(new Intef("3NG"))5
Toast.makeText(this, "Intent enviada!",Toast.LENGTH_SHORT).show();
}
HeIIoReceiver.java
import android.content.BroadcastReceiver;
public class HelloReceiver extends BroadcastReceiver {
@0verride
public void onReceive(Contet c, Intent intent) {
Log.d("livroandroid", " HelloReceiver !!!");
}
Agora que voc j foi apresentado a mais uma classe do Android, pense um pouco
na arquitetura de Intent e IntentF1ter. Lembre-se de que a Intent representa uma
mensagem com a inteno de realizar algo, e o IntentF1ter o que ltra essa men
Sgftm por ao e categoria. Agora j sabemos que esse ltro pode ser configurado
para executar uma Activity ou um BroadcastRecever.
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
ndViewById(R.id.btEnviar).set0nClickListener(this);
// Registra o receiver
registerReceiver(hellokeceiver,new IntentFilter("BINGO"));
}
@Override
public void onClick(View v) {
sendBroadcast(new Intent("BINGO"));
Toast.makeText(this,"Intent enviada!",Toast.LENGTH_SHORT).show();
}
@0verride
protected void onDestroy() {
super.onDestroy();
// Cancela o receiver
unregisterReceiver(hellokeceiver);
}
s| p11
android:1ayout_wdth="match_parent" androidzlayout height="natch_parent"
android:padding="16dp" android:orentaton="vertca1">
<TextVew android:d="@+d/text"
android:tet="C1que no boto para disparar um receiver"
androd:layout_width="wrap_content" androd:1ayout_heght="wrap_content" />
<Button androd:d="@+d/btEnvar" android:tet="Enviar"
androd:1ayout_width="wrap_content" androd:1ayout_height="wrap_content" />
</LnearLayout>
A gura 24.1 mostra o resultado deste exemplo. Ao clicar no boto para disparar
a mensagem, ela interceptada pelo receiver dinmico e o Textvew atualizado.
EDmamicoli
I
r
amam l
il
1
Clique no boto para dispara um receiver | Mensagem recebida pelo HeiloRer;ewer i
* Euvuua
'l7 ..1'
que voc entendeu o conC1I0
. , ` AndroidManiest.xml.
- f gurado no arquivo A sua principal
Um receiver estatico e COD
` ' terceptar os eventos sempre, mesmo se a
vantagem ' dessa
que forma ele vai in
aplicao estiver fechada. I _ _ .
d. . ico Congurado programaticamente no codigo da activity
Ja um recener m - 'd est
vi atrelado
a ao da activity Isso e geralmente uti
, - agens internas
Nesse caso, o seu ciclo de
' dentro da prpria aplicao.
lizado para enviar lTln5
563 Google Android - 4= edio
24.5 Classe LocalBroadcastManager
Mensagens de broadcast so enviadas para todas as aplicaes instaladas, mas s
vezes o que queremos somente enviar uma mensagem de forma local na aplicao.
Para isso, recomendada a classe android.support.v4.content.LocalBroadcastManager.
// Cancela o receiver
manager.unregisterReceiver(helloReceiver);
<recever androd:name="
.SuaC1asseReceverAqu">
_ - - 9 y- "/>
<intent-1ter>
</intent-1ter>
<acton androd:name="android.ntent.acton.BO0T COMPLETED" />
<category android:nane="androd intent cate or BEFAULT
</recever>
. , - stRecever
24.10 Mostrando uma noticao para o usurio
Para nalizar este capitulo, gostaria apenas de' lembrar QU Lim Bmadca
ou
me .
no deve interagir com o usurio de forma direta, seja exibindo um alerta ou
abrindo uma tela sem a sua perm1SS20
V ser mais elaro- um receiver no deve atrapalhar o usurio! Nunca chame uma
` e e bem rovV C1 . , .
activity de dentro de um receiver. Lembre-se de que o usurio pode estar fazendo algo
1 ue ele no deseje ser incomodado. No existe nada mais
mconveni
importlnilte do qui aplicaes malfeitas que ficam atrapalhando a vida do usuario.
A manei ndada de interagir com o usurio por meio de uma noti
ra reco ca na barra de status do Android, o qual chama a
_ z . render o conceito.`Quan
(zao, que um alerta qL1 do voc recebe um email
. l uma tela
sua atenao. E simples de 1'1 indesejada
- ~ 1 por acaso ap8fC 3 8
na tela do seu
pela aplicaao do Gmal ,
672 Google Android - 4= edio
d'~^
celular? A resposta no, pois o Gmail mostra uma agradvel notificao na barra
e status, e essa noticaao voce pode ver quando puder ou quiser.
Aproveitando a conversa, notificaes o assunto do prximo captulo.
https://developenandroid.com/training/run-bac/eground-service/report-status.html
` A' CAPTULO 25
Notication
\
l
25.1 Por que usar uma noticao para se comunicar com o usurio
Uma aplicao executando em segundo plano nunca deve exibir um alerta para
o usurio, ou, pior ainda, abrir uma tela sem a sua permisso. Lembre-se de que
o usurio pode estar lendo emails importantes, estar em uma chamada com um
amigo, jogando ou executando qualquer outra aplicaao.
Um exemplo de utilizao de notificaes seria uma aplicao de chat, na qual o
usurio pode receber uma notificao quando chegar uma nova mensagem. Ao
Selecionar a notao, o usurio pode decidir visualizar a mensagem ou nao.
Quzrg exemplo
atualizao de seria um servioNo
informaes. quemOIT1f0
executa em segundqplano,
em que 3 3fU3 Ifa-zeqralagiia
123930 efm '
~ ~ ' suario
caao poderiautilizando uma o not1 u tipos
._25.1
'informar
f ~ ' ' ca
~ l do.Android
uma documentaao OC13
' de ovarios
A gura
noticaoes.
que exibe
_ _mostra
__
. d Android 41 35 noticaes podem ser criadas com mais contedo,
A pamr O l , Outra melhoria do Android 4.1 foi o suporte s
chamadas de Big View Notications
aoes -diretamente
~ t1caOS nas nO
- ~que Saocustomizados que aparecem
botes
na notificao.
673
\
Q um . amar eus.
New Goog|e+ notifications 5 t4 W
i 'lL.:,"d Added .~ track J
II
Screenshot captured. JI' >M
jul/ him v PRN v: ^"I" ". '
Keep photos 8. videos backed . 4 . . .fm
iy: zt ' PP j2'|-,.'1r- *.l'jP Hr (~';f;-;!l~
3 new messages 4 1 w
' " O
lzurvratar:~a\n:vJn.am;ffi;;Tia 0 z
Q 3(52)263
new0?<'
messages
fiat
z 4; wi
/res/layout/activity_main.m|
<LinearLayout mlns:android="http://schemas.android.com/apk/res/androdu
android:layout_width="match_parent" android:layout_height="matCh parent"
android:padding="16dp" android:orientation="vertical"> _
<TetView
android:text="Eemplo de noticaes"
android:layout_width="wrap_content" android:l
ayout _height="wrap_content" />
<Button
android:layout_width="match_parent" android:layout_height="wrap_content"
android:
<Button tet="Noticao Simples" android:onClick="onClickNoticacaoSimples" />
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Noticao Heads-up" android:onClick="onClickNoticacaoHeadsUp" />
<Button
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Noticao Big" android:onClick="onClickNoticacaoBig" />
<Button
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Noticao com Ao" android:onClick="onClickNoticacaoComAcao" />
Repare que cada boto declara a tag android:onClick, portanto ao clicar neste boto
o mtodo congurado ser executado na activity Voc pode adicionar os mtodos
manualmente no cdigo da activity ou utilizar o assistente do Android Studio
para fazer isso automaticamente. A primeira forma de abrir o assistente clicar
na lmpada amarela no cdigo-fonte do arquivo de layout. Essa lmpada um
indicador de que o assistente pode ajud-lo com algo. Para e>c1bi-la,.que com o
cursor na linha em que voc escreveu o cdigo com a tag androidzontlick. A segun
. - - "' ara criar o mtodo.
da forma utilizar a tecla de atalho AIt+Enter tambm nessa linha. Uma vez que o
assistente for exibido, basta escolher a oPa0 P
, f ~ ' ` car assim:
. . ' '
Depois de criar os metodos, O C0d18 da acuvlty deve
MainActivity.java
public class MainActivity extends ADDCPatACt1V1 y {
@0verride _ _
. ' ' l.
public void onCreate(Bundle icicle) {
} - ' 'w){}
super.onCreate(icicle)5 _n)
setContentView(R.laY0Ut-actlvltyrma
.. ' 'ew){}
public void onClickNoticacaoSimples(VIEW Vle
public void onClickNoticacaoHeadsUp(View V1
676 Google Android - 4 edio
public void onClickNoticacaoBig(View view) { }
public void onClickNoticacaoComAcao(View view) { }
}
A primeira noticao que vamos fazer a mais simples e pode ser utilizada em
tod as as verses do Android, pois mostra apenas um cone, ttulo e mensagem.
Para encapsular a criao das noticaes, vamos criar a classe Noticationutil,
conforme o cdigo demonstrado a seguir:
NoticationUti|.java
public class Noticationtil {
// Cria a Pendinglntent para abrir a activity da intent
private static Pendinglntent getPendingIntent(Contet context, Intent intent, int id) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Esta linha mantm a activity pai na pilha de activities
stackBuilder.addParentStack(intent.getComponent());
// Congura a intent que vai abrir ao clicar na noticao
stackBuilder.addNetIntent(intent);
// Cria a Pendinglntent e atualiza caso exista uma com o mesmo id
Pendinglntent p = stackBuilder.getPendingIntent(id, Pendinglntent.FLAG_UPDATE_CURRENT);
return p;
}
nm . cancelAll( ); '
public static void cancellAll(Context context) {
Nt1cauMaa9e"C0WDH DFI = NotificationManagerConpat.fron(contet)'
l
}
.'~0u|..,
Nota: o mtodo setefaults (Notification.DEFAULT ALL) da classe NoticationCompat.Builder
copgurar a noticao
as uzes. ecnicamente comaum
falando, som padro,
constante faz vibrar oaplica
DEFAULT_ALL celular e ainda
todas acende
as constantes
DEFAULT_SOUND, DEFAULT_VIBRATE e DEFAULT_LIGHTS. Como nesse caso o celular vai vibrar
e preciso declarar a permissao android.permss1.on.VIBRATE no arquivo de manifesto.
O cdigo-fonte dessa classe est comentado, portanto leia-o com ateno. Basica
mente estamos utilizando a classe Notificationtonpat.Builder para criar a noticao
com os parmetros informados. Note que alguns parmetros esto fixos, como o
cone da notificao, que deixei com o mesmo cone do aplicativo. O importante
criar a intent e a Pendinglntent que ser disparada ao clicar na noticao. A
Pendinglntent um tipo especial de intent que encasula uma intent real, a fim de
ser disparada ao clicar na notificao.
Para concluir este primeiro exemplo, altere o mtodo onClickNotifcacaoSinples(view)
para chamar o mtodo NotifcationUtil.create( . . .) e criar a noticao simples.
MainActivity.java
} _ __..
public class MainActivity extends AppCompatActivity f
int id = 1; H
public void onClickNotifcacaoSinples(View vi-GW) {
o 1 rca i z '
ntent.pUtExtra(..mSg , 013, Leitor, como vai )
N ff' fonutil create(this intent,contentTitle,contentText,id);
}
MensagemActivity.java
public class MensagemActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_mensagem);
getSupportActionBar()_setDisp1ayHomeAsUpEnab1ed(true);
String msg = getIntent().getStringEtra("msg");
Textview text = (Textview) ndViewById(R.id.tet);
tet.setTet(msg);
}
/res/Iayout/activity_mensagem.xml
<LinearLayout . . .>
<TetView android:id="@+id/text"
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content" />
Para este exemplo funcionar corretamente, importante que seja feita a con
gurao da activity MensagemActivity e de sua activity pai no arquivo de manifesto.
Veja que configurei as tags android:parentActivityName e <meta-data indicando que a
activity pai na hierarquia a MainActivity.
AndroidManifest.xmI
<appiication . . .>
<uses-permission android:name="android.pernission.VIBRATE"/>
<activity android:name=".MainActivity" ... />
<activity android:name=".MensagemActivity"
android:parentActivityName=".MainActivity" >
<meta-data android:nane="android.support.PARENT_ACTIVITY"
android:va1ue=".MainActivity" /
Captulo 25 n Notication 679
Nota: lemb
re- ' ' ~ ~ . .
I I _ Sede adicionar a permissao VIBRATE no arquivo de manifesto. Isso
e necessario, pois a notifica o cria
f ` `adcom o mtodo setDefau1ts(Notification.
O1
DEFAULT_ALL) .
1li zlII
O resultado a notificao exibida na figura 25.2. Ao clicar na noticao, a intent
que foi configurada com a activity MensagemActivity disparada, consequentemente
esta tela mostrada ao usurio. A mensagem Ol, Leitor, comovai? foi lida dos parmetros
da intent, apenas para demonstrar que podemos enviar os parmetros.
O interessante das noticaes que elas podem executar mesmo com o aplicativo
fechado. Portanto, depois de disparar a noticao, feche o aplicativo. Ao clicar
na notificao, voc ver que a activity MensagemActivity ser chamada. Agora faa
o teste, e clique no boto voltar. O resultado que a MainActivity foi exibida, cer
to? Mas se a aplicao estava fechada, e chamamos a MensagemActivity pela intent,
como fizemos para colocar a MainActivity na base da pilha? Isso foi feito no mtodo
getPendingIntent() da classe NotificationUti1, com a ajuda da classe TaskStackBui1der.
A linha de cdigo a seguir adiciona a activity pai na pilha. Se voc comentar esta
linha, somente a classe MensagemActivity ser adicionada pilha.
// Esta linha mantm a activity pai na pilha de activities
stackBui1der . addParentStack(intent . getComponent( ) );
Essa uma deciso de design importante, pois na maioria das vezes, ao abrir uma
activity tambm precisamos carregar a sua activity pai.
' Exemplo
Ol Leiton como vai? 1
de noticaes I
NOTWFICAO com A
A
u
l 99998877 ,riL\~!1i.1LJ z*
Dica: uma heads-up notification pode ser removida fazendo o gesto de swipe lateral.
ou seja, segure-a e arrestc para a direita, jogando a notificao para tora da tela.
(aPtu|o 25 I Notication
681
Para criar uma heads-u noti '
10" "\Dt.Builder. P - f
boolean) da Classe Nolidcafcilnbasta chamaro mtodo setFullScreenIntent(Pendinglntent,
C|_eateHeadSUpNocat_ i _ ara testar o conceito, crie o metodo
10( - -) na classe Notif|cationUtil.
NoticationUtiI.java
public class Noticationtil {
nm.notify(id, b.build());
}
MainAttvity.java
public class MainActivity extends ADDC0'lD3tACtlVty f
3
` 'ss.4.Ancl em
Nova mensagem simples '
'fl' \- L ' Ml ;'iW.; mk \ l if Jfi'
Exmpio de
Q Nmiricwmrmosw
Nurmcmlo BIG
NornF|cA;o cowsclo
MainActivity.java
public class MainActivity extends AppCompatActivity {
}
Captulo 25 I Notication 683
Para, Cutomiaar o modo de visibilidade da noticao na tela de bloqueio, utilize
O melo 0 SVSU/(int) da classe NottficattonConpat.But1der, informando uma das
seguintes constantes:
e.
' en
fe-e-'*'"`"~'Ww'JwV S Conguraes do 5i5[ema na op0 Sound 8*
Ateno: no Android 5.0,wntree is locked para voc configurar como deseja
Configuration > Notficatton > blo ueio se comportam. se O usurio tiver uma
) NoticationUtiI.java
public class NoticationUtil {
No cdigo foi utilizada a classe InboStyle para congu rar o estilo da noticao C
adicionar vrias linhas na mensagem. Agora, atualize o cdigo da classe HainActivity
para criar a noticao grande.
Captulo 25 n Notication
685
MainActivity.java
Neste exemplo, criamos uma lista com trs mensagens, e ao clicar no segundo
boto o resultado ser como a gura 25.6. Repare que no canto direito inferior da
noticao apareceu o nmero 3, pois no cdigo utilizei o mtodo setNunber(int)
a.classe Noticationtompat.Builder.
l| _l
Exemplo de noticaes
NoTu=|cAo s|M||_ss
i No1'|F|cAo HEADS-UP
F'zguf -'
a 256 Exemplo com noticao expandida
636 Google Android - 4 edio
25.6 Criando uma noticao com aes
Para criar uma noti cao com ao, basta chamar o mtodo builder.addAction(icone,
string, intent) congurando o cone da ao, o texto e a intent que ser disparada
ao clicar na ao.
Para o prximo exemplo, crie o mtodo createwithAction( . . .) na classe NoticationUtil.
Repare que resumi o cdigo, pois a forma de criar a noticao igual ao exem
plo da noticao simples, com exceo de que estou chamando o mtodo
builder.addAction(icone, string, intent).
NoticationUtiI.java
public class Noticationtil {
public Static nal String ACTION_VISUALIZAR = "ACTION_VISUALIZAR";
-3 ManActivity.java
};
@0verride
|
..
NoT|F|cAco suv||1.Es
NOTIFICAO HEDS-UP
, NOTIFICAO BIG
NOTIFICAO HEADSHUP
NoTu=|cAo BIG
NOTIFICAO oomAO
F'zgur
zz 257-ExemP0
' d "0' mm o
533 Google Android - 4 edio
Ao clicar nos botes, uma intent disparada. A terceira parte da gura mostra o
alerta informando que o broadcast receiver recebeu a intent. Lembre-se de que
usei a mesma intent e receiver para ambas as aes para simplicar o cdigo, mas
na prtica voc teria uma intent e receiver para a ao pause e outra para o play.
E para criar uma Pendnglntent que vai disparar um broadcast para um receiver assim:
Pendnglntent p = Pendnglntent.getBroadcast(ths, 0, new Intent("ABRIR_APLICACAO_TESTE"),
Pendnglntent.FLAG_UPDATE_CURRENT);
e .
_ ast e 1 . .
259 Exemplo mm "t'(a0 E BroadtastReteiver
No captulo anterior, criamos um ro.
a
.__g
como envlar uma intent por broadcp 1 to chamado HelloReceiver que mostrou
broadcast recelver. nterceptar a mensagem utilizando um
. ~ , os a r1morar
vam ' es
Para exercitar o conceito de n oticaes
. 61' rcce er a mensa em Para '
uma noucaao quando O receiv b P se exemplo e mostrar
projeto HeIIoReceiver do captulo ante ` g . Commuar, Cople O
rior e renomeie para HeI|oReceiverNotication.
Andro dM . , s
Este projeto j declara um receiver esttico que est confi urado no
. 1 amespt xml para interceptar as mensagens com aao BINGO. Como queremos
criar uma noticaao, adicione a seguinte classe ao projeto'
NoticationUtiI.java
public class Noticationtil {
public static void notify(Context context, int id, Intent intent, String contentTitle,
String contentText) {
NoticationManager manager =
(NoticationManager) context.getSystenService(Contet.NOTIFICATION_SERVICE);
// Intent para disparar o broadcast
Pendinglntent p = Pendinglntent.getActivity(context, 0, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
// Cria a notication
NoticationCompat.Builder builder = new NoticationCompat.Builder(contet)
.setContentIntent(p)
.setContentTitle(contentTitle)
.setContentTet(contentText) .
setSmallIcon(R.drawable.ic_notication_icon)
.setAutoCancel(true);
}
l
.~e
// Dispara a notication
Notfkaton n z builder.build();
nanager.notify(id, H);
. Cdigo
MainActivity.java
public class HainActivity extends AppCompatActivity implements View.0nClickListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ndViewById(R.id.btEnviar).set0nClickListener(this);
readHsg(getIntent());
1
@0verride
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
readHsg(intent);
}
P L`
...fxx
1. " `*.` bai
Captulo 25 u Notication 691
Toast.nakeTet(this "Voc di it
} else { ' 9 = " + P1S9,Tost.LENoTH_sHoRT).shw();
Toast.m k T " ... .
} a e et(this, Extras. + intent.getEtras(),Toast.LENGTH_SHORT).show();
}
@0verride
public void onC1ick(View v) {
EditTet text = (EditText) findViewById(R.id.tet);
String msg = tet.getTet().toString();
. IlTVT1Ti1
Intent intent = new Intent(new Intent("BINGO"));
intent.putExtra("nsg",nsg);
sendBroadcast(intent);
Toast . nakeTet(this , " Intent enviada! " ,Toast . LENGTH_SHORT) . show( );
}
A gura 25.8 mostra o resultado desse exemplo. O uxo simples: o receiver vai
receber a mensagem e mostrar a noticao. Quando o usurio clicar na notica
o, a MainActivity ser chamada e neste momento estou lendo o parmetro "msg
que chegou da intent.
l Ca! W 7 z V '
;1LDigite
0_ um
Digite
l ENVIAR
umtexto
R_ texto e dispara
doe dispara
Qi Ricardoa intent
a intent 4 _ _12
ENVIAR
on ' ~
.
. - I- ' ci .
1
I
lsso mantm apenas urna HainActtvity na pilha. Caso a activity no exista, ela
ser criada; caso contrario, a sua instncia ser preservada em memria e o m~
todo onNewIntent(intent) sera chamado. Exatamente por isso o codigo faz a leitura
do parmetro "msg" em dois lugares diferentes: nos mtodos onCreate(bund1e) e
onNewIntent(intent).
Nesse mtodo, voc pode informar o valor maximo do progresso. corno 100. e
depois ir atualizando os valores para informar o status de determinado proces
samento diretamente na noticao. Esse conceito pode ser muito utilizado em
conjunto com servios que estao fazendo um processamento em segundo plano.
O metodo setProgress(ma, progresso, innito) tambm contem um flag do tipo
booleano. Caso voc informe true, os valores maximo e atual serao ignorados e
uma animao contnua sera exibida. Esse parmetro deve ser utilizado quando
voc no souber ao certo quando o processarnento sera finalizado.
A figura 25.9 mostra como seria a notificao com a barra de progresso normal e
com o flag infinito para true. Na figura da esquerda, o valor informado foi 5()*`t
com o seguinte cdigo:
bui1der.setProgress(100, 50, false);
http://developer: android.com/gaide/topics/ui/notiers/noticatiorzs.html
Android Training - Notifying the User
http://developer arzdroid.com/design/patterns/notications.html
Android DevBytes: Noticaes no L Developer Preview [Portuguese]
694
Captulo 26 u AIarmManager 695
26.2 Mtodo da classe AIarmManager
Para a endar um ala ' ' .
pod 8emos vera classe
rme, utiliza-se os mtodos mais
classe. im ort d O a
android . app.AlarmManager
P antes essa
Na lista a seguir
cancel(PendingIntent intent)
.cria
`~osii' o,
5eto
m
Cancela o alarme baseado na intent fornecida, que deve ser a mesma
se , automa
l rme .
?
aS _
C) , C1 t(tpo dataH0ra intent) e setRepeating(tipo,dataHora,intervalo,intent)
~ undo ainda e capaz de repetir o a a
fazem a mesma coisa, mas O Seg
lado. Independentemente de como o alarme
ticamente durante o tempo estip ' 1,40
' d b ta chamar o mtodo cancel(intent) para cance a .
'di o ue dispara
Omo.' larme
Para demonstrarcriar um
Csegue
emacaa 21
` d um
30 ums:
_ de co g q
trecho
HHH
o alarme exatamente as 9.00 am e rep
private AlarmManager alarmMgr;
t alarmlntenti
private Pendin9"te
// L o servio do AlarmMa89@f
Google Android - 4 edio
ELAPSED_REALTIME
ELAPSED_REALTIME_NAKEUP
Idem constante anterior, mas neste caso a CPU acordada para receber
o alarme, mesmo se o dispositivo estiver no modo de espera. Por padro.
os alarmes s so recebidos se estiverem ativados.
Dispara um alarme em uma data e hora especca que pode ser configu
rada no cdigo.
RTC HAKEUP
AndroidManifest.xmI
<manfest ... package="br.com.lvroandrod.helloalarme" >
<applcation . . .>
<actvity androd:name=".ManActvty" androd:launchMode="sngleTop" ... />
receiver androd:name=".LembremeDeConerRecever">
<intent-lter>
<acti.on androd : name="br . com . livroand rod . helloalarne. LEMBREME_DE_COMER" />
<category androd:name="androd.ntent.category.DEFAULT" />
</ntent-lter>
</recever>
</applicaton>
LembremeDeComerRecever.java
oticatontil;
import lvroandrod.lb.utls.N - ds BroadcastRecever {
- D ComerRecetver et
, , . TAG - ltvroa
publtc class Lembreme e . _ H . ndrodn;
prlvate Statlc nal String z "br.com.lvroandrod.helloalarme.LEMBREME_DE_COMER";
public static final String ACTION
593 Google Android - 4 edio
@0verride
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"Voc precisa comer: " + new Date());
Intent notifIntent = new Intent(context,MainActivity.class);
Noticationtil.create(context, 1, notifIntent, R.mipmap.ic_launcher,
"Hora de comer algo...","Que tal uma fruta?");
}
O receiver vai interceptar a intent que ser disparada pelo alarme e vai mostrar
uma noticao. A classe Notiticationutil voc pode criar como foi feito no pro
Jeto anterior, ou se preferir utilize a classe livroandroid.lib.utils.NoticationUtil da
biblioteca android-utils.
Para disparar os alarmes, vamos criar a classe AlarmUtil. O objetivo desta classe
apenas encapsular o acesso classe AlarmManager do Android.
AIarmUtiI.java
public class AlarmUtil {
private static nal String TAG = "livroandroid";
// Agenda o alarme na data/hora informado.
public static void schedule(Context context, Intent intent, long triggerAtMillis) {
Pendinglntent p = PendingIntent.getBroadcast(context, 1, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
Alarmanager alarme = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarme.set(AlarmManager.RTC_wAKEUP, triggerAtMillis, p);
Log.d("livroandroid-alarm", "Alarme agendado com sucesso.");
}
// Cancela o alarme
public static void cancel(Context context, Intent intent) {
AlarmManager alarme = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Pendinglntent p = Pendinglntent.getBroadcast(contet, 1, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
Captulo 26 1 A|armManage 699
alarme.cancel(p);
L09.d("livroandroid al " "
- arm
/res/layout/activity_main.xmI
<LinearLayout . . .>
<TextView android:tet="Agendar o alarme" . . . />
<Button android:tet="Agendar para 5 segundos" . . .
android:onClick="onClickAgendar"/>
<Button android:tet="Agendar e repetir a cada 30 seg" . .
android:onClick="onClickAgendarComRepeat" />
<Button android:text="Cancelar"
android:onClick="onClickCancelar"/>
Por ltimo, no cdigo da activity vamos adicionar os trs mtodos para tratar os
eventos.
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Inten
public
.. "){
long time z .getTimeInMilliS();
return time;
. C1 kA endar(View view .
void 02iniceaew
= Intent(LembremeDeComerReceiver.ACTI0N);
// Agenda Para daqui g 5 segundos
T ' );
AlarmU til.schedule(th1S, Intent' ge* 1me()
700
Google Android - 4 edio
Toast.makeTet(this,"Alarme agendado.",Toast.LENGTH_SHORT).show();
}
l
Intent intent = new Intent(LenbremeDeConerReceiver.ACTION);
A1arnUtil.cance1(this,intent);
Toast.nakeText(this,"Alarme cancelado",Toast.LENGTH_SHORT).show();
}
CANCELAR
Alarme agendado
l
FKHHI-/UUHNUCMHIHHUUL
f9
Captulo 26 n AlarmManager 701
26.4 Repetindo o alarme
No cdigo que z
. . eemos Para repetir o alarm
a cada
Caso voc prera utilizar cons
30 segundos foi passado o
tempo em milissegundos, portanto zemos
long INTERVAL_HALF_HOUR = 2 * I
a co ,
nta 30 * 1.000.
NTERVAL_FIFTEEN MINUTES;
INTERVAL_HOUR = 2 * INTERVAL_HALF_HOUR; _
INTERVAL_HALF_DAY = 12 * INTERVAL_HOUR;
INTERVAL_DAY = 2 * INTERVAL_HALF_DAY;
Portanto, para agendar um alarme com a opo de repetir todo dia, poderamos
utilizar a constante AlarmManager.INTERVAL_DAY.
// Agenda este alarme com a opo de repetir todos os dias no mesmo horrio.
AlarmUtil.scheduleRepeat(this, intent, getTime(), AlarmManager.INTERVAL_DAY);
_.-~
long time = c.getTimeInMillis();
f aula
nao e de
dar Calendar, ate porque isso e assun ' ` ' to para
1 '/ bre
S Ca lin 'Mas
uagem Java . ~con
Meu objetivo aqui uma
7 apenas para dar uma dica, essa classe
um hvro ; ntrolar facilmente datas e horas, assim como fazer operaoes
como
tem metoaumentar
O P ma horauaumentar
um dia etc. Outro exemplo interessante
data de hoje s 9:30 am.
como congurar o Calendar com 8
();
Calendar calendar =.Calendar.getInstance'
tTimeM'uS());
1
calendar. setTimeInMill1S(5y5te'cume"
Y, 9 5
calendar . set(Calendar . H0UR_0F_;2)'
calendar.set(Cle"dar'MINUTE' , )
lon9 time = C-9@tTeI"M11S();
702 Google Android - 4 edio
Se voc quiser que esta data seja criada apenas amanh, basta somar um dia ao
criar no Calendar.
Como eu disse, meu objetivo no dar uma aula sobre Calendar; portanto, caso
voc no conhea essa classe, recomendo que a estude.
https://developer android.com/training/scheduling/alarms.html
J ~* CAPTULO 27
Service e Joblnfo
`*1
27.1 Introduo
A classe android.app.Service utilizada no Android para executar um processa
mento em segundo plano por tempo indeterminado, chamado popularmente
de servio. Um servio geralmente faz um alto consumo de recursos, memria
e CPU. No precisa interagir com o usurio e consequentemente no precisa de
interface grfica.
Geralmente um servio com a classe Service iniciado a partir de um BroadcastReceiver 7
704
Captulo 27 n Service e Joblnfo
.._emoria --
705
de threads
ao e ' - '- ,' ,
27.2 Exemplos de servios
Geralmente, studar servios pela primeira vez, e comum confundir o conceito
e servios, pois ambos executam de forma assincrona e teoricamente
em segundo plano.
Caso prera disparar o servio com uma ao, utilize a tag <ntent-1ter> para
congurar a ao que deve disparar o servio. O atributo androd:eported="fa1se"
indica que esse servio privado da aplicao, de forma que outras aplicaes
no podem utilizar essa intent para inici-lo.
<ntent-1ter> '
<servce android:name=".He1loService" android:eported="fa1se">
HeIIoService.java
_ , . -~ ' Util;
import livroandroid.lib.utils.Notif|cat10
public class HelloService extends Service {
Drivate static nal int MAX = 195
TAG z "livro";
private static final String
protected int count;
private boolean running;
Google Android - 4 edio
@0verride
public IBinder onBind(Intent i) {
// Por enquanto vamos deixar null. Depois vamos estudar isso.
return null;
1
@0verride
public void onCreate() {
Log.d(TAG, "HelloService.onCreate() - Service criado");
}
@0verride
public int onStartCommand(Intent intent, int ags, int startld) {
Log.d(TAG, "HelloService.onStartCommand() - Service iniciado: " + startld);
count = 0;
// Mtodo chamado depois do onCreate(), logo depois que o servio iniciado
// 0 parmetro startId representa o identicador deste servio
running = true;
// Delega para uma thread
new NorkerThread().start();
// Chama a implementao da classe me
return super.onStartCommand(intent, ags, startld);
}
}
}
@0verride
running = false;
ere 9 Para a thread parar (isso ' '
// para encerrar a thread caso al ' . E Importante
QUEM tenha chamado o stopService(intent)
AndroidManifest.xmI
<appiication . . >
<activity android:nane=".MainActivity" ... />
<service android:nane=".He11oService" /
0. .~'
fazendo com que o AndrOd Ch ame o mtodo onDestroy(), encerrando o processo
para liberar memria e recursos utilizados. Outra forma de parar um servio
simplesmente cham ando 0 mtodo stopService(intent).
, . . - 1o.
ao usuario que a execL1 1. tivo
6 qU21Hd0 VOC Instala
iniciado para baixar O HP , _
ica pelo Google Play. Nesse caso, um servio
un? ap. ' 1 "o termina uma noticaao
l1cat1vo, e quando a msta aa
e utilizada para avisar ao ullfg F _ O
710
Google Android - 4 edio
Para mostra r como iniciar e parar o servio, vamos criar uma activity com os
botes Start e Stop no layout.
/res/layout/activity_main.xml
<LinearLayout android:orientation="vertical" ...
<TetView
MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
}r
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Pronto! Feito isso, execute o projeto e clique no boto Start para iniciar o servio. O
resultado da execuo voc pode conferir no LogCat, que deve mostrar as seguintes
mensagens, que mostram o servio executando e incrementando o contador de O
a 10. Voc pode inclusive sair da aplicao que 0 servio vai continuar executando.
pois esse o seu propsito.
Captulo 27 n Service e .oblnfo
711
D/1ivro( 2606): He1oService.
onCreate() - 5 ervice criado
D/1ivro( 2606): HeioService.
onStartConnand() - Serv ice iniciado: 1
D/1ivro( 2606): He1oService
executando...
D/iivro(2606): He1oService executando...
D/1ivro(2606): HeioService executando...
D/iivro(2606): He1oService executando...
D/1ivro( 6): He1oService executando...
D/1ivro(2606): He1ioService executando...
D/1ivro(2606): He1oService executando...
D/1ivro(2606): He11oService executando...
77.
D/1ivro(2606): He11oService executando...
D/iivro( 6): He11oService executando...
D/1ivro( 6): Hei1oService m thread: 10
D/1ivro(2606): He1ioService.onDestroy() Service destruido
Observe que o prprio servio encerrou seu processo com a chamada do mtodo
stopSe1f(). Neste exemplo, caso o boto Stop seja chamado antes do m, o servio
ser nalizado antes de o contador chegar a 10.
Embora para acompanhar o resultado deste exemplo voc deva olhar no LogCat, a
figura 211 mostra a noticao que criada quando o servio terminar por conta
prpria ou seja o contador chegar a 10. Noticaes so muito utilizadas nesses
casos para avisar ao usurio que um servio terminou, se for algo que ele estiver
esperando, como uma atualizao de dados do aplicativo.
r_c;gCa. l
Exempo de servuz *@5fUe os WQS no l
START
STOP
e, Cimo. .
,
0nCreate()
StartComman<1() i
if,...-....-_.r
_&~\*
,xoService
running'
\`^----`--....--~..
. ,.. of V
j onDestroy()
f "-~.
Unbounded
service
co service,
am ele _ _
classe me seja chamado. Indica que, caso o sistema encerre
ser recriado, e o mtodo onStartComnand(. . .) ser
h ado novamente Mas, neste caso, a intent recebida como
parmetro ser nula. Se voc no precisar dessa intent, essa
constante dever ser utilizada.
START__REDELIVER_INTENT
Idem anterior, mas a mesm a intent que iniciou o service
entregue novamente. Se o service utiliza a intent para dife
renciar o contedo, como cada thread pode fazer o upload
' ` ' m ido.
de diferentes arquivos, utilize essa constante para receber a
intent n OV2lI`I'1I'l[, C3SO O SCl'VlC SCJH 1I`l[I`I`O p
714 Google Android - 4 edio
Nota: no importa quantas vezes o mtodo startService(intent) cha mado: uma
umca chamada ao mtodo stopService(intent) nalizar o servio.
Conhecer o ciclo de vida da classe Service importante, pois a maneira como voc
vai gerenciar as threads dentro do servio vai depender do que voc precisa fazer.
Se voc deseja que exista apenas uma thread, poder cri-la no mtodo onCreate( ),
de forma que novas chamadas no afetem a thread que j est executando.
Voc tambm pode criar uma nova thread a cada chamada do mtodo
onStartCommand(intent,ags,startld), mas deve se preocupar com o fato de o cdigo
ser multi-threading. Nesse caso, o parmetro startld pode identificar a thread.
Esse parmetro startld deve ser armazenado para encerrar o servio com o m
todo stopSe1f(startId). Veja que esse mtodo recebe o id da thread que precisa ser
interrompida, diferentemente do stopSe1f(), que utilizado para destruir todos
os servios.
7 7 S7
IntentService faz o que provavelm ente voc teria de fazer se ela no existisse.
Outra vantagem da classe IntentService que ela chama automaticamente o mtodo
stopSelf () ou stopSelf(startId) no nal da execuo' portanto como eu disse ante
voce so precisa implementar o mtodo onHandleIntent(Intent intent).
O cdigo de exemplo a seguir mostra como implementar um servio com a classe
IntentService. Estamos utilizando o mesmo exemplo do contador de O a 10. Note
que no preciso criar uma thread nem chamar o stopSelf(), pois o IntentService
j faz tudo isso internamente.
He||oIntentService.java
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("NomeDaThreadAqui");
}
@0verride _
private boolean running;
} , f. u ) ,
count++;
} 'sa() f
L0g_d(TAG, "ExemploServico IN-
@0verride
public void onDestroy() {
super.onDestroy();
// Ao encerrar o servio, altera o ag para a thread parar
running = false;
Log.d(TAG, "EemploServico.onDestroy()");
}
Para testar esse servio, basta execut-lo, e o resultado ser o mesmo do exemplo
anterior.
startService(new Intent(ths, HelloIntentService.class));
Mp3Service.java
public class Mp3Service extends Service implenents Int f
private static nal String TAG z "1vr0". er aceMp3 {
private PlayerMp3 player = new p1ayerMp3E). S
}
@0verride
public IBinder onBind(Intent intent) {
// retorna a classe ConexaoInterfaceMp3 para a activity utilizar
Log.d(TAG, "Mp3Service onBind(). Aqui retorna o IBinder.");
return new Mp3ServiceBinder();
}
@Override
public void onDestroy() {
// Fim do servio
Log.d(TAG, "Mp3Service onDestroy().");
// Para a msica
5D()
}
InterfaceMp3.java
public interface InterfaceMp3 {
void play(String mp3); // Inicia a msica
void pause(); // Faz pause da msica
void stop(); // Para a msica
boolean isPlaying(); // Retorna true se est tocando a msica
String getMp3(); // Caminho da msica
}
f fw .Y
Na prtica a1Vlty
-D33
Captulo 27 n Service e Joblnfo
TVI O O " `
act' ' vai se conectar ao se
terface Interfacem 3 h I bI` U.I'I`l8. I`fI`l'lCl para 3. lfl
D 6 C amar os seus metodos 1
vamos partir para o cdigo da activity que a ymp
parte,mais
pauseo, Stopo etc. Agora
complicada.
719
MainActivity.java
import livroandroid.lib.utils.NoticationUtil;
public class MainActivity extends A ppConpatActivity {
private static nal String TAG = "livro";
// Classe que encapsula o MediaPlayer
private EditText text;
private InterfaceMp3 interfaceMp3;
private Serviceonnection conexao = new ServiceConnection() {
public void onServiceConnected(ConponentNane classNane, IBinder service) {
/I (*3*)
// Recupera a interface para interagir com o servio
Mp3Service.Mp3ServiceBinder conexao = (Mp3Service.Mp3ServiceBinder) service;
interfaceMp3 = conexao.getInterface();
Log.d(TAG, "onServiceConnected, interfaceMp3 conectada: " + interfaceMp3);
}
// ** ._
public void onServiceDisconnected(ComponentNane className) {
};
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle); _
setContentView(R.ly0Ut~aCt1V1tyfam)'_ _
text = (edttrext) fndvewById(R-ld-t^1)'
Intent ntent z new Intent(this,Mp3Service.class);
Log.d(TAG, "Iniciando o service");
I/ (*1*)
startService(intent)
// Faz o bind/l9H
/I (*2*) .c-r~rt,BIND Auro0"CREATE);
conexao. 9'-
boolean b = bindService(inten H b
L09.d(TAG,"Service conectado: + )5
}public void
V'ew view) {
onClickPlaY( 1
I/ (*4*)
if(interfaceMp3 != null) {
720 Google Android - 4' edio
String np3 = tet.getTet().toString();
Log.d(TAG,"play: " + np3);
interfaceHp3.play(np3);
}
@0verride
protected void onStop() {
super.onStop();
if(interfaceMp3 != null && interfaceMp3.isPlaying()) {
// ("5*)
Log.d(TAG, "Activity destruda. A msica continua ");
unbindService(coneao);
// Cria a noticao para o usurio voltar ao player.
String mp3 = interfaceMp3.getMp3();
Noticationutil.create(this,1,new Intent(this,MainActivity.class),"HP3 Player",mp3);
} else {
/l (*7*)
Log.d(TAG, "Activity destruida. Para o servio, pois no existe msica tocando.");
unbindService(coneao);
stopService(new Intent(this, Hp3Service.class));
}
/sdcard/Music/linkin_park1.mp3
* DU rj
. , eOredo
(2) Conecta ao serviom de tudo.
arametros um_J_I _
Pfqul esta O S g A Qbeto que implemente a interface
todo bindService(intent,con,ags) pre
./v'd
(7) Encerrando o servio caso no esteja tocando nenhuma msica
No mtodo onStop() da activity caso a activity no esteja tocando nenhu
ma msica, no tem razo para deixar o servio executando em segundo
plano. Por isso, o mtodo stopService(intent) chamado para encerr-lo,
liberando os recursos e memria. i
Esse exemplo avanado, mas demonstrou como uma activity pode se conectar
a um servio que esta executando em segundo plano. Vimos que por meio 21
interface de comunicao, a qual exposta pelo servio, a activity pode invocar
mtodos l dentro da classe do servio.
Entenda que o servio iniciado pelo mtodo startService(intent) e permanecc
executando at que o mtodo stoptService(intent) seja chamado. Sempre qu 0
activity for iniciada, independentemente de o servio j estar executando OU
AO
.eza
_.
encerrar aXamv.
Isso signica ue Qd
'
(aPtuIo 27 n Service e Joblnfo 723
no, o mtodo bndService(intent,con,ags) utili d
, _ o para se conectar ao servio.
0 metodo unbtndService( COHH) e chamado para desconectar.
troasse
da erC1 odser
a inter ace ~todo
. , .den
. _e gcomunica '
Vezes a m deqobtp mosfnos onectar e desconectar de um servio
ao e chamar al umdiversas
me
t S _ VIO- E, quando o servio nao for mais necessario, o mtodo
S OP @FV1C@('C't) Chamado para encerrar tod o o processo.
Parmetro Descr _
ntgnt Intent para executar o servio.
Ca Imp , ' Connected(c1asse,1 tn e
emen da interface android . content. ServiceConnection, que dene os
1 rao
'b' d r)eonServiceDisconnected(classe)
metodos onServtce
~ ~ hamados para noti`car que uma conexo com o servio
05 quais sao C
foi realizada OU encerrada, respectivamente.
ags _ ' sario. E se for BIND_AUTO_CREATE, o servio automaticamente
OU BIND_AUTO_CREAT 2 I _
criado, caso seja neces
.c10 de vida de um servio executado com o mtodo
A gura 274 mostra O C1
bindService(intent,Ca95)
724 Google Android - 4 ediao
cum q
`_ ~ '- yr' ,.'r, ..\z..a.
\` .. .. .. .`-$*...l.,<.-z.ia..z_...':/
onCreate()
l
onBind()
i
' Clients ara
,i.
bound to
service
l
OD68lI'Oy0
l
Service
HL lyhilt down _,
Boundod
servico
tem de verifica , ~ . .
Ima ine ue seu serviO
ecise baixar Pfservidor
atualizaes de um I _Web,_mas_antes
g q r se existem atualizaes disponiveis. Ele poderia car dormindo
de minuto em minuto Pa ra realizar essas vericaoes, o que poderia consumir
muito processamenw
- ' duas ^maneiras
' r.' lver essadesituao
Basicamente existem
resotudo
, depende na
verdade do que VOC Pfeclsa faze
72 owgiznnami-4zd;
A primeira maneira utilizar um alarme criado com a classe androd.app.A1armHanager
e agendar uma intent para acordar o servio na data desejada. Os alarmes pode
riam ser utilizados para executar o servio de hora em hora, ou executa-lo todos
os dia s meia-noite, por exemplo. A maneira que gosto de fazer disparar um
alarme que vai acordar um broadcast receiver, que por sua vez apenas inicia o
servio chamando o mtodo startService(ntent).
A segunda maneira o servio no car buscando dados no servidor, na tentativa
de verificar se existem atualizaes. O melhor seria o servidor enviar uma mensa
gem de push para o dispositivo, para avisar aplicao que existem atualizaes.
Vamos estudar sobre mensagens push no prximo captulo, mas basicamente
o push uma mensagem enviada do servidor para o dispositivo, a qual utiliza
a conexo com a internet como canal de comunicao. No aplicativo, o push
recebido por meio de um broadcast receiver. Neste caso do servio, quando a
aplicao receber a mensagem de push, ela poderia buscar os dados no servidor.
Isso economiza recursos e bateria do dispositivo, e bem mais eficiente do que
car de tempos em tempos perguntando ao servidor se existem atualizaes.
Voc^ * ' . . .
O dispositivo entrou em modo bloqueio.
O dispositivo est carregando a bateria.
e talvez nao esteja interessado na hora que o job vai executar, mas sim nas
condioes/criterios sob as quais ele ir executar. Sendo assim, podemos execu
tar o job sempre que o dispositivo estiver plugado e com uma conexo Wi-Fi
disponivel. Esta API e de grande ajuda em muitos casos, mas compatvel com
Android 5.0 ou superior. Na poca em que este livro estava sendo escrito, no
existia nenhuma biblioteca de compatibilidade, porm quem sabe o Google logo
no construir uma?
Para agendar um job, basta criar um objeto do tipo Joblnfo utilizando o
JobInfo.Builder. Feito isso, basta obter uma referncia da classe JobScheduler e criar
o job, representado pela classe Joblnfo. Para brincarmos com a API de Jobs, crie
um projeto chamado Helloloblnfo. Feito isso, crie a classe JobUtil, que mostra como
agendar um job para executar assim que o dispositivo estiver conectado ao Wi-Fi
e estiver carregando.
JobUtiI.java
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class JobUtil {
public static void schedule(Context context, Class<?> cls, int id) {
// JobService que vai executar
ComponentNane mServiceConponent = new C0F1D0@'CNF1(C0t, Cl-S);
JobInfo.Builder builder = new Joblnfo.Builder(id,nServiceConponent);
// Ni-Fi
builder setRequiredNetworkType(JobInfo.NETHORK_TYPE_UNMETERED);
// Carregando
builder.setRequiresChargin9(FUE);
// Agenda o j0b
h d l r obScheduler =
Jobsc e(oScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
Joblnfo job = builder.build();
jobScheduler . schedule( job);
}
728 Google Android - 4 edio
public static void cancel(Contet context, int id) {
JobScheduler jobScheduler = _ . .;
jobScheduler.cancel(id);
}
Com essa classe utilitria em mos, vamos criar o layout e o cdigo da activity
Teremos apenas um boto para agendar o job e outro para cancel-lo. Deixarei o
layout com voc. Basta criar dois botes e chamar os mtodos onClickAgendar(view)
e onClickCancelar(view), conforme demonstrado a seguir.
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
}
Esse cdigo vai agendar o job da classe HelloJobService para executar assim que
o dispositivo estiver carregando a bateria e com uma conexo Wi-Fi disponvel.
A classe HelloJobService deve ser lha de JobService e deve implementar o mtodo
onStartJob() responsvel pela lgica do job. A classe JobService_por sua vez lha
direta de Service, portanto tudo o que estudamos sobre servios continua se apli
cando. No mtodo onStartJob(), voc deve iniciar uma thread para desvincular 2
tarefa do job da thread principal da aplicao.
A seguir, podemos visualizar o cdigo-fonte da classe HelloJobService.
CaPtuIo 27 n Service e Joblnfo
729
HeIIoJobService.java
public class HelloJo
bService extends JobService {
private static nal Stri"9TAG = "livroandroid";
@Override
@Override
@Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob(): " + parans.getJobId());
Intent notIntent = new Intent(this,MainActivity.class);
String title = "Job";
String contentTitle = "Hello Job: " + parans.getJobId();
NoticationUtil.create(this, R.mipmap.ic_launcher, notlntent, R.mipmap.ic_launcher,
title, contentTitle);
return true;
}
}...
@Override
public boolean onStopJob(JobParaneters DFWS) {
Log.d(TAG, "onStopJob()");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy()");
}
_ f. tJob()
. . . _e onara
P _I. _
_ , mesmos mtodos de ciclo de vida da classe Service.
Note que O Job Contem Os Sto Job() so especcos para informar quan
Apenas Os metodos onstar p finalizar a congurao, basta adicionar a tag
do
aoOAndroidMcmfe5t-xm
Job Val lmclar 6 parar le informara permisso BIND_JOB_SERVICE,necessaria
Pronto! Isso tudo. Agora execute o projeto no seu Android e clique no boto
para agendar o job. Veja que com o celular plugado na USB e com Wi-Fi o job vai
executar. Neste exemplo, o job est somente mostrando uma noticao, apenas
para voc saber quando ele executou.
Esta API nova, mas tem grande potencial. Sua documentao ainda escassa,
e ca at difcil recomendar uma leitura, mas espero que em breve exista algo na
documentao ocial. Recomendo tambm importar no Android Studio pelo
menu Import Sample o exemplo lobScheduler, que com certeza vai ajud-lo.
http://developexandroid.com/guide/components/services. html
YouTube Google Developers - Using the Android Job Scheduler
731
732 g1zAniwii-- iam
*`\`\`"\\ No Caso do (imail. o servidor do Lioogle envia uma mensagem de push
informando que existem novos einails disponveis e que o dispositivo pode, ento.
Iniciar uiu \veb serviu: para fazer o sincmnismo mm o servidor.
P590 Phkvsso de o servidor enviar uma mensagem para o celular conhecido
\""\\* Plh. v o servico responsavel por iinplementar essa tarefa no Android chama
-Se Gooqleoud Messaging ou. simplesmente. GCM.
Para enviar uma mensagem de push para o dispositivo e preciso obter o seu codigo
unico. chamado de registration id. Para isso o aplicativo deve fazer uma consulta no
servidor do GCM e obter o registration id. conforme ilustrado na gura 28.1.
'**Y ' il E
' ..f '_ 1. * 1 -\
..
z' -~ .'i 1-t
_3.\z_"- leiblvu
*I * /'
z* 7 . s il?? ._
' . . ii_):.i4, i z
:xp \. _
. - _.
L.:..-, \sn;Ibsvvw~-.|i.oinn~u\==*l\.dQ-nIv-1.(;"\l$_I
, -..l fz. \ QT
*X ^ I~ .
_ negz>tzat'.o ED
VJ.
-_ f 1 ` `_.1z \V ~,
.. .................,............,. _.....z...)
,- **
.W _..\\5i
%< zz___3 leltlfew U ?=Cl\`i
;:_ -..~wvf=-- 1..e;~-z~~~z.z ... z~.-z-~-=~,-z~zz.~zz~ -.~-.~~.--..n-z --- 4- ,
_.
2) Entrega mensagem
1) Envia mensagem
Canal de comunicao
, va i ,I
I| I.I
Figura 28.3 - Habilitando o GCM.
E da mesma forma que geramos uma chave para a aplicao dos mapas, desta vez
vamos gerar uma chave para 0 servio do GCM. Contudo, essa chave no ser
gerada para a aplicao cliente no Android, mas sim para a aplicao que ser o
servidor do push, a qual vai enviar as mensagens para o dispositivo.
Portanto, entre no menu Credentlals para criar u ma nova chave de acesso, chamada
de API Key. Lembre-se de que voc j deve ter uma chave API Key feita no captulo 22,
sobre mapas. Clique no boto Create New Key para criar uma nova chave e escolha
a opo Server Key. Na prxima janela, voc pode restringir os endereos IPs dos
servidores que podem enviar as mensagens de push. N nosso exemplo, deixe
este campo de texto vazio e clique no boto Create.
A gura 28.4 mostra a chave criada. Copie o cdigo do API Key antes de prosseguir,
pois vamos utiliza-lo posteriormente no servidor para enviar a mensagem de push.
lQ
l
- Google
~-~~-
jLivro
180dOP projeto
'
Gl' ` f .L
. .
- .__
Overview
onso e, entre
l
"' ' * ~z~
4M_._
Android
- `l
A _.
.W
na pgina
conhecido
gura 28.5. como Project Number, conforme a
_a.
' > Overview do
Request
P5 3 l-h Requestss
Biing settings
i,-_._ ao se Wa.-a.._--....- -W W =
Figura 28.5 - Obtendo 0 Project Number
li
ra O P - ^ ' d Goo le
de ser jegal VermOS a mensagem de push chegando ao dispositivo.
Constantsjava
package br.1ivro.androd.CP23~PU5h5
public interface Constants f I . 8 do Google Developers console
// Project Number criado na DHQW
999559052058"$
String PROJECT_NUMBER = "
}
/lb
Google Android - 4 :digno
_-1 ~.
* 1*H|\#l|H.\*\llH|ug|Irmz(.( ML'zregismltinnidhw||pl|\'u|1||u.~,
*l l* LJ H 'BW- HU |'.u uHlIlH.l. || Iegifin id quai .|mlu L' |I:z lug lu
|\l;(a.l.|1ul\ \.HllH^lll||I./J u p.xl.l f1l\'I.ll` .1 lllLll.l}{3ll|.
)
ligam ._8.(~-l'L4c1J Ifxjllu mv (A M
lu uhl)pu`lll\l do la n'1
Nu H ml |l\\.|Tn|llU1\`|l\|U uu|l.\1uu|r,zu(Ily,u\l|;;leqitldlinl
. _lllm
, .x lll\'l1>J;\ill zum U* |1xI'\lHs'll"~. ||.\ URI xlzllllkl-1
.Mu |\u.u .a\\,.|Iu\l.z |u|ul^|.1 HI ll u'uuul| Aulhu uauou., mu \.zl1
kuyAPl hay |'.ll.l \|\'llhll*\|~|I \'l|\ 'wul u~\ uNly wytlu' Hill.: Iu[|rl|\ Il\.u..l\
n\|||z.mI\ I.\\.\.
Captulo 28 I GCM - Google Cloud Messaging
SendPushMessage.java
public class SendPushMessage {
// Registration id do dispositivo
// Cria a mensagem H H
}
// Faz Posr _ _, _
byte[] postgata z postB0dy.t0String().9etBY@5( UTF'3 )
L^ a reSD05a .
conn.setReQUSPF0P@ftV( Aut O
return null;
}
Para testar esse cdigo, crie um projeto java em alguma IDE, como o Eclipse. Lembran
do que uma classe java pode ser executada no Eclipse pelo menu RunAs>Java Application.
Mas antes de executar o cdigo altere a constante DEVICE_REGISTRATION_ID para o valor
que voc copiou do seu dispositivo ao se registrar no GCM.
// Registration id do dispositivo
private static nal String DEVICE_REGISTRATION_ID = "APA91bGZNbjA2zeXelrPA26PeA ..... ";
Altere tambm a constante API_KEY com a API KEY que voc copiou do seu projeto.
// Chave criada no Console. Menu > API Access > (create new server key)
private static nal String API_KEY = "AIzaSyCMCGjvjeoAmI8jllDH4388EJI4qJFZsU";
Pronto! Agora execute o cdigo para enviar a mensagem de push para o disposi
tivo. Ao receber a mensagem, o aplicativo vai mostrar uma noticao, conforme
mostra a gura 281
Para nalizar, importante saber que a requisio HTTP enviadaao servidor do
GCM pode retornar os seguintes cdigos:
Retorno HTP Descrio
E E Indica que a mensagem recebida cm sucesso, e o servidor do GCM
tentar entregar a mensagem.
461 Indica que ocorreu um erro ao autorizar o envio da mensagem. Neste
caso, voc deve vericar se a chave utilizada est correta.
5 Qualquer cdigo de retorno entre 500 a 599 indica que ocorreu um
erro no servidor do GCM.
_
Captulo 28 n GCM - Google Cloud Messaging 739
REGISTRAR CANCELAR
Ol Leitor
app/buiId.gradIe
' ' ' . , _ ' :7.0.0'
Compile .comg00g1e_andro1d.gms.play services
74 oaagiennaria-4-ediao
28.8 (Iasse GoogIe(IoudMessaging
No Codigo do aplicativo, para obter o reglstratlon Id, basta utilizar o mtodo
f9Ster(projectNunber) da classe Googleloudessaging do Google Play Services:
// Registrando o dispositivo no GCH
Googletloudessaging gcm = Googletloudnessaging.getInstance(context);
String registrationld = gcn.register(Project Number Aqui);
ff!-S G(M.java
return registrationld;
}
} saveRegistrationId(context,registrationld);
L09-d(TAG, "<< GcM.re
QSFHFO OK, registration id: " + registrationld);
return registrationld,
} catch (IOEception e) {
L0g.e(TAG, "<< GCM.registrar() ERRO: " + e.getMessage(), e);
}
return null;
}
az _ _ , ,
}
Utilizar a classe GCM simples. Note que necessrio informar o Project Number para
fazer o registro Portanto, crie no projeto uma interface ou classe abstrata chamada
Constants com a constante PROJECT_NUHBER.
iri Constantsjava
package br.livro android.cap28.push;
public interface Constants {
// Project Number criado na pgina do Google Developers Console
string PRoJtcT_uu-een = "999S590S2058";
}
AndroidManifest.xmI
<?1 V@FS0="1.0" encodin9="utf-8"?>
<af@5 PCkage="br.com.livroandroid.hellopush" , , _>
<. Para receber a mensagem precisa de internet -->
<uses-permission android:name="android.permission.INTERNET" />
_
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity android:name=".MainActivity" . . .>
No manifesto, declaramO5 21
receiver chamado GcmBr08
dcas
a 55 MainActivity como de costume, mais um broadcast
tpecever e um service chamado GcmIntentService.
7" coqtznemu-4-mu
^ ***R""'~ Psllios ver o codigo do broadcast receiver que tem como ohieti
vo interceptar as mensagens de push. Iisse receiver esta interceptanclo a ao
WH-99le.androld.c2dn.tntent.RECEIVE. a qual e enviada por broadcast pelo sistema
quando chega utna nova mensageni de push.
ifri GcmBroadcastRece|ver.]ava
Un GcmIntentService.java
SUner(constants.PRoJEcT_NuMaEn);
}
@Override
n_
String msg = extras.getString("S9");
L .d TAG, msg); _ _
i.''
Ioen ntent _ new Intent(this, MainActivity.class);
ntent.DUfEtr("mS9" 59)5
Notication t.1 create(th5 1 intent,R.drawable.ic_notication_icon,"Nova
mensagem",mS9);
}
s
Importante: o construtor da das e GcmIntentService recebe como parmetre a
PR _ '
Constante Constants. OJEU NUMBER que 0 Project Number que copiamos na paglna
deconaedO(300gk'
74 aoogiznnami-4~zar1
No mtodo onHand1eIntent(intent) da classe GcmIntentService estamos lendo a mensa
80111 d push que chega via a intent. O cdigo est verificando o tipo da mensagem
6 chamando os mtodos onError(bund1e), no caso de erro, ou onHessage(bundle), no
Caso de sucesso. Atente-se para os tipos das mensagens, pois no futuro o GCM
pode ser expandido e novos tipos podero surgir.
Um fator importante sobre a lgica do cdigo-fonte que no final do
mtodo onHand1eIntent(ntent) e chamado o mtodo GcmBroadcastRecetver.
completewakefullntent(intent). Isso feito para liberar o bloqueio do processador
(wale lock), o qual foi travado no passo anterior pelo receiver GcmBroadcastReceiver.
No mtodo onHessage(bund1e) recebe-se a mensagem. Neste caso estamos lendo um
parmetro "msg" do tipo String, pois o servidor que vamos criar vai enviar uma
mensagem exatamente com esse parmetro. Logo depois de ler a mensagem, o
aplicativo vai mostrar uma noticao para o usurio. Ao clicar na noticao, a
ManActvity ser aberta para ler a mensagem.
private void onHessage(Bund1e extras) {
I/ L a mensagem e mostra uma notcao
String msg = etras.getStrng("msg");
Log.d(TAG, msg);
Intent intent = new Intent(this, HanActivty.c1ass);
ntent.putEtra("msg", msg);
Notcatontil.create(this,1,intent,R.drawable.ic_notcaton_con,
Nova mensagem", msg);
1
f /res/layout/activity_main.xml
LnearLayout mlns:androtd="http://schemas.androd.con/apk/res/android"
android:Iayout_width="match_parent" androtd:1ayout_hetght="match_parent"
android:padding="16dp" androtd:orentaton='vertica1>
LinearLayout android:orientation="vertca1"
android : i.ayoutwdth="match_parent " android : layout_hetght="urap_content"
Captulo 28 n GCM - Google (loud Mes
<Button
Soio 747
androidzl ' :H H ,
andrd_t:k\th WcD_content android:layout_height="wrap_content"
(Button - 91Strar android:onClick="onClickRegistrar" />
an:r?d:layou-Wdth="WfD_C0tent" android:layout_height="wrap_content"
. an roid.text= Cancelar" android:onClick="onClickCancelar" />
<TetView android:id="@+id/text"
android: y0Ut_F9"T0D="20dp" android:tet="Clique em registrar."
3"dfd313Y0Ut_Wdth="wrap_content" android:layout_height="wrap_content" />
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static nal String TAG = "livroandroid";
@0verride
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Verica se o Google Play Services est instalado
boolean ok = checkPlayServices();
if (ok) {
// J est registrado
String regld = GCM.getRegistrationId(this);
setText(regId);
/ Quando iniciar, l a msg da noticao
String msg = getIntent().getStrin9EfFH("S9");
setText(mS9);
}
@0verride _
}
ama u ,,"
1 tent(intent);
protected void onNewIntent(Intent intent) {
rin ~ '
// L a msg da notica0
St ` 9 ms9 ' intent 9etStrin9Etfa("S9");
setTet(l'lS9)3
}
private Context 9etCtet() {
mounu-qu
Fllf thlg
I
ll Nostra nag dn qqqg gg f|\
hflvt vold x0tTent(nal Strln ) {
llla Q null) {
fvnlibroidlu Runnabloli {
O veffldo
public void runl) {
Textvlou text E (fextvlou) ndVlouiyld(R.ld.tont)z
\oxt.stTet(s):
log.d(IAG.s):
)
}):
l
I
/I Fac o reqlslro no GCM
publlc vold onCllckRo9latrar(vlou vlow) {
nen Throod() {
Qverrlde
ou llc vota run() {
supef.run();
String rogld Gt.QotoglstrtlonId(9etontet());
lf (regld 35 null) {
ll Faz o roqlstro Q pego o reglslratlon ld
fegld = QflFQQSOf(QGCOK(). Constants.PROJECT,NUN!ER):
set\ext(Reglstrado con sucesso.\n + rQgId);
) else {
tost('voc j est reglstrdo\):
1
I
).stort():
l
/1 fnclo o reqlslro no Gta
pub\\ vol! onCl\k|ncolr(V\|u vlou) (
nem IhrQd() l
0vorldQ
ubllc vold run() {
mw \run( )
0N~UlfI\\\f(|('\\\())l
gQ1ot(Concoldo con suso1')
l
}.xlar():
)
(aPtu|o 28 n GCM - Google (loud Messaging 749
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
toast("Este dispositivo no suportaoog
o Ge lPl ay Services.);
nish();
}
return false;
}
return true;
}
});
}
Como a- 'MainActivity
' de ser aberta por
pOuma
.. . ~ ~ ura ~ HO1
ao.
adicione o atributo
'cao
android:launchMode= S19l@TP asua Con g
. . - .android.name . - - I-
z" MainActivity ' - hMode="singleT0p"
android.launc . . . />
,, -3- me , .
<activity
ao sera A
na notl
~ e o Android crie apenas uma ins
elembrand0, 6553 Cogufaao faz com qu - ~ tivit aberta
Apenas r . . d ue se o usurio clicar na noticaao com a ac Y _
tncia da act1v1t} Sen O q ,tcdo 0nNew1ntent(intent) ser chamado. Sem esse atributo,
` . reutilizada
elaclicar caoa e Sern
O pre uma nova activity seria criada e colocada na pilha.
. . se ' registra ..
A _ , Sabe como eleve ser feito o cdigo-fonte do aplicatiV0
E isso e tudo! Agora voce J Sa eng de push ge vge fez tudo passo a passo, execute
Android para receber as rnenr o GCM_ Depois, com o registration id do dispositivo
o a llC8t1V0 Para
m
p a mensagem por push utilizando o cdigo Java que
em maos, P0
j_n1ostran1O5
\
azgienumi-4-aim
p Salvandooestadoda
_,,,,___.
aplicao
Entender o ciclo de vida de uma activity ou fragment um fator decisivo para
construir um aplicativo de sucesso.
Neste captulo, vamos entender quando uma activity pode ser destruda e re
criada, e como o aplicativo deve salvar o estado das informaes. Provavelmente
voc j conhece o conceito, pois j mostramos casos simples sobre como salvar
informaes durante a troca de conguraes de sistema, mas este captulo visa
complementar os seus estudos.
Se voc quiser, pule este captulo. Eu s preciso garantir que voc domine o as
sunto, pois misso dada misso cumprida.
751
752 awgiznamia-4-zuiao \
hsses tres casos so exemplos de troca de con gurao, e, se alguma dessas condi
oes ocorrer, o Android vai encerrar a activity atual e vai recri-la logo em seguida
para que a nova congurao faa efeito.
Veja que at que faz sentido. Vamos dizer que voc tenha um layout para vertical
e horizontal, conforme demonstrado a seguir:
/res/layout/activity_main.xmI
' /res/layout-Iand/activity_main.xml
Neste caso, 0 identicador land de landscape e representa o layout horizontal. O
sistema do Android vai utilizar o layout correto conforme a orientao do dispo
sitivo. Quando a activity criada pela primeira vez na vertical (portrait), o layout
/res/layout/activity_main.xml utilizado. Ento, ao girar a tela para horizontal (landscape), o
Android vai destruir essa activity e recri-la novamente, mas desta vez quando o mtodo
onCreate(bundle) for chamado o layout utilizado ser o /res/layout-land/aaivity_main.xm1.
O Android apresenta esse comportamento porque muitas vezes preciso atualizar o
layout em casos de alteraes nas conguraes do sistema.
Com os fragments, o conceito o mesmo, e o mtodo onCreateView() pode inar uma
view diferente dependendo da orientao. justamente por isso que a activity e
seus fragments so destrudos e recriados, assim voc tem uma chance de recriar
a tela com as configuraes adequadas.
Como desenvolvedor, voc deve estar ciente desse comportamento e conhecer
como salvar o estado da aplicao nesses casos.
iii Fragmentljava
public class Fragnentl extends 0ebugFragnent {
private int count;
@0verride
public View onCreateView(Layoutlnater inater, Viewroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_1, container, false);
nal Textview t = (Textview) view.ndViewById(R.id.text);
Captulo 29 n Salvando o estado da aplicao 753
// Recupera o estado
f(savedInstanceState != null) {
COUH = savedInstan
ceState . getInt( "count");
// Atualiza o Textview com o valor salvo
t.setText("Count: " + gount).
}
view.ndViewById(R.id.btOk). to
@Override se nClickListener(new OnClickListener() {
@Override
s..
super.onSaveInstanceState(outState);
// Salva o estado
outState.putInt("count", count);
}
}
Nota: a classe Bundle como uma HashMap e salva os objetos por chave e valor. Ela
pode salvar vrios tipos, como Integer, Boolean, String, Serializable, Parcelable etc.
1nser *
E5 e fra ment tem um simples layout com um boto que ca incrementando o
Cntadg Note que este ele lho de DebugFragment. A activity desse fragment apenas
' e O fragment no layout sendo que ela lha de DebugActivity.
Estou fazendo essas heranas P Qrque quero reforar a ordem da chamada dos
mtodos do ciclo de Vlda
Portanto, faa O Seguinte teste:
. - Incrementar cinco vezes.
1. Execute O pf0J 6 clique no botao
, va
2 O lor do contador ser atualizado para 5
. ire
3 G' a tela do dispositivo (110 @mu1df (m+m)`
4. Depois de glfaf
. on
tela oavalor
' do contador deve continuar como 5.
ltado O mtodo onSaveInstanceState(bundle) salvou o
A gllfa 29-1 mostra O resu . tador lido na inicializao do fragment.
estado do apli cativo, 6 0 Valor do C
754 Google Android - 4' edio
Fragmentl .java
public class Fragmentl extends CicloVidaFragment {
@0verride {
private int count;
- t , Viewroup C0
public View onCreateView(Layoutlnater l ef
Bundle savedInstanceState) i tamem false);
View view = inater.in ate(R-lYUt-fra9ment'1' Co
75 ooqtzrtnafaia-4~ata
final textviw t n (Textvtn) vtew.ndVtovById(R.td.t|t);
/I 0 atributo count vat permanecer en nondrto
t.||tTeirt(Count: " + count);
vtew.ftndVtevById(R.td.bt0k).set0nC1tckLtstener(nev 0nCltckListener() {
O0verrlde
public votd on(tick(vtew v) {
count++;
t.setTet("Count: " + count);
1
i);
return view;
1
I``|l, lLi:lll
para mostrar uma animao que ca girando, ou uma barra que pode incrementar
o progresso de uma tarefa. Isso depende do estilo que for utilizado.
O cdigo a seguir mostra como criar um ProgressBar com uma barra horizontal:
<ProgressBarandrod:id="@+d/barraProgresso" style="?androd:attr/progressBarStyleHorzontal"
androd:1ayout_width="match_parent" androd:1ayout_heght="wrap_content"
androd:na="100" androd:progress="0" />
A gura 29.2 mostra o exemplo que zemos no captul0 7, que apenas inicia uma
thread que ca incrementando um contador de O a 100 e atualizando o valor do
progresso.
1 e -e A t 1
l Barra de ProgreSSO
IITIU
| ______.__.__._.~
S_ l narefa "
SimularTarefa
- p
Barra de Progresso
3 i ---*' .
l
11l l
lI
1_
Figufd 29 2 ~ Thread executando.
758 Goglehlitlritl-*edio
Mas o que acontece se voc girar a tela durante o andamento dessa tarefa? Sabe
IUUS que a activity vai ser destruda e recriada, ento o que acontece com a tluearl
que estava executando?
No existe nada que vai parar a thread durante a troca de orientao; assim. caso
voc queira parar a thread, voc deve interrompe-la no mtodo onPause( ) ou onStop()
do ciclo de vida da activity. Mas neste caso no queremos interromper a thread. e
sim deix-la executando, pois a tarefa precisa continuar independentemente da
troca de orientao.
Portanto, no exemplo do capitulo 7, ao girar a tela. a activity destruda, porm
a thread continua viva e executando. Mas como essa thread est associada com
urna activity que ser destruda, se ela atualizar a view nada vai acontecer. Quan
do a nova activity for criada, o Progressar apenas vai mostrar o ltimo valor que
recebeu a atualizao, pois as views do Android internamente tambm salvam
seu prprio estado. Porm, a thread se perde no meio do caminho, e o problema
que ela est associada com uma activity que j no est mais sendo mostrada
ao usurio. Inclusive isso um problema, pois devido a essa referncia a activity
antiga no pode ser eliminada de memria pelo garbage Collector e tivemos um
memory leak. Mas esse um assunto mais avanado, e vamos deixar pra l.
O que ns queremos deixar a thread viva, mas, assim que a nova activity e view
forem criadas, queremos atualizar o progresso da tarefa, como se nada tivesse
acontecido. Tudo deve funcionar perfeitamente, mesmo se o usurio car brin
cando de girar o celular.
O modo indicado de solucionar esse problema utilizar um fragment e reter a ins
tncia com o mtodo setRetainInstance(true). Feito isso, a thread dentro do fragmenr
pode continuar executando sem problemas, e assim que a view do fragment for
criada depois da troca de orientao podemos atualizar o status do progresso.
A thread permanece viva, e apenas o fragment vai mostrar urna view diferente.
O cdigo a seguir mostra a soluo para esse problema, e com ele vamos conseguir
girar a tela sem parar a tarefa e a atualizao do progresso.
r: ProgressBarDemoActivity.java
} new ProgressBarDemoFragment()).commit().
}
/WS/|aY0U/CVity_progress_bar_demo.xmI
<FV3meLy0U XNlS2dr0id="http://schemas.android.com/apk/res/android"
mlns:tools="htt p://schemas.android.com/tools"
andmd113)/0Ut_Wdh="P1Ch_D8I'ent" android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
a"dfd=d="@+id/layoutFrag">
Como voce pode ver, a activity no faz nada, e mais uma vez vamos delegar a
lglca para o fragment.
ProgressBarDemoFragment.java
public class ProgressBarDemoFragment extends Fragment {
private int progress;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // Deixa o fragment em memria
}
@Override
public View onCreateView(Layoutlnater inater, @Nullable Viewroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_progress_bar_demo, container, false);
Button b z (Button) view.ndViewById(R.id.bt0K);
b.set0nClickListener(new Button.0nClickListener() {
@Override
public void onClick(V@W view) i
executarTarefa();
0.
}});
return view;
}
private void executarTarefa() i
new Thread(new Runnable() i
public void run() i I _
/ A varivel Df9"e55 ca em memoria
ll O f r Comea em 0 na primeira vez ou continua de onde parou
for (int i = PF9f555 <= 100; 1++) {
I/ Salva o estado do int progress
progress = i;
updateProgressBar();
try {
Thread.sleep(56);
} catch (InterruptedEception e) {
}
}).start();
}
});
}
de condi 1 (1 - _
So ch ama os.Isso acontece depois que os metodos onDestroyView( ) e onDetach( )
ctivity
. ~ Mas como a thread contmua executando, temos de fazer um teste
o e va 1 ar se a view do fragment existe e se o fragment est associado
com a activity Portanto, adicionei esta condio no cdigo:
f(9@VeW() != null && !isDetached()) {
getView().post(new Runnable() {
// O fragment est ok, podemos atualizar a view
});
}
Se voce entendeu o que aconteceu, timo, pois esse um dos grandes benefcios
de se reter a instncia de um fragment. Se voc no entendeu neste momento, sem
problemas, mas quando achar necessrio revise este exerccio, pois com certeza
isso vai lhe ser muito til algum dia.
or p ,
P adro se voc no fizer isso, o Android vai permitir as orientaes vertical e
horizontal. Para forar que U ma activity que apenas na horizontal, basta utilizar
a constante landscape
<ac tvi ~ ' 1.35seActivityAqui" android:screen0rientation="landscape" />
t. .ty and|_od.name-"
762 wgiznmia-4-ediao
Nota: atualmente, segundo as boas prticas do Google, recomendvel deixar o
aplicativo girar a vontade, pois o usurio deve poder escolher qual a orientao
que ele prefere ao utilizar o aparelho. Lembre-se de que o usurio quem manda
no celular dele, e no voc.
Sempre que o usurio abrir ou fechar o teclado, a activity ser destruda e recriada
da mesma forma que ao girar a tela. Ento tudo que voc aprendeu sobre salvar
o estado da aplicao tambm se aplica neste caso.
Lembro-me at hoje de quando fui fazer uma demonstrao na Motorola e meu
aplicativo travou quando o gerente abriu o teclado do seu Milestone. Na poca
eu no conhecia esse comportamento e tambm no tinha me precupad em
salvar corretamente o estado da tela.
' ^ f
e conse u
~ I 1
(aPtuIo 29 u Salvando o estado da aplicao 763
manualment .A Y i
emos os responsveis or oalta 1out e atualiz-lo tudo
p erar
antagem de ter 0 controle total e que a activity nao e destruida
Sf Ci mente os objetos permanecem em memoria. Portanto, nao e neces
f os dados ou busca-los novamente. Parece uma boa 1de1a, nao e? Se
e or o seu obietivo, voce teria de ter u ma conversa mais ou menos assim com
o nosso simptico sistemapoaciona
er ~` 1odrobozinho verde:
Desenvolvedor: Fala, Android, como est?
Existem de Conguraao
eventos
~ `torados mas es
para aV5-l0 qual configurao foi alterada.
_ , . varios que podem ser moni ,
- 350 mais
tamos interessados nO C comum,
~ ' 0 OFW"
taton
que ocorre ao trocar a
orientao do celular. Neste Ca 50, voc pode congurar a activity com o parmetro
androd:configChan9@5="e"taun '
' ' "' droid:congChanges="orentaton"
. . - . =" C1asseAct1v1tyAqU1 Em />
quanto do 6
sa'
<act1v1.ty andro1d.name .
Caso voce^queira
' brescrever
do
SO
do
O C0mP0f tamento tanto da troca de orientao
teclado isso poderia ser feito assim:
*9f\1V\\Y ndroid:n0o~".tlass|ActtvttvAqut"
"d'\df""9Ch|ng|s~"ortontatton|keyboardutdden " /
Feitiu twi, vuee eleve suhreserever u metudu on(onqur|ttonChan|d(conguratton) mt
au tivuy Se vuee der uma rztpidzi uiizilismiu mt classe Configuration. ver que 6 pusslvel
dere|1t'ir tt ettigttrzi; que muduu: se fui ai riet1ttu;u. idiumn (lueale) uu u
estudu du teeludu litftr (ubertu uu ecitatdu).
0()vr r ide
Nu vuu ttlur maus muitu dissu uqui. puis seguutlu us iwuus pr'tticts du /\ttdruid
HU|`(``()i`l1(`II(|.ItiH$Hi3I`(`S(`l'(`VL`|'L`S*;\'~ L`lI|1i).',ll!tI(`)t'5.C este breve t'pic lui upettts
ptrt tlitfl t lu suhre tssu.
Istuu explicuudu issu put-t|ue e eumutu eueuutt~'ur sites, lilugs etc. expliemdu
esse nssuutu. purtzmtu ieu aqui miuhn dteu ptru vue evttur uu mtximu uti|tr.:u~
esse reeursu, it mtu ser que u|u,utu diu vue tenhu um hum mutivu. e sztilm u que
estt iu/emlu.
Reeumeudu s;i|v.tr u cstndu du urrliczttivu utiiirztudu us metudus
onSavetnstaneeState(bundle)tfsetRetatnlnstauc(true) dttwuuietu.Issj hitexrdteuf
du e euuiurme vimus mtu e ttu dtiteil ussitu.
a ap icao 755
Captulo 29 n Salvando o estado d I
29.9
Salvando o estado no projeto dos carros
Enm, este breve tpico foi apenas para lembr-lo disso, j que falamos sobre
salvar o estado da aplicao. Mas salvar o estado no projeto dos carros vou deixar
para voc, caso queira fazer algum exerccio.
http://developer android.com/guide/topics/resources/runtime-changes.html
` cAPruLo 30
Suportando diferentes
i""~, tamanhos de telas
Neste captulo, vamos aprender os conceitos necessrios para criar aplicativos que
funcionem em diversas telas e resolues. Voc aprender o conceito de densidade
e a notao dp (density-independent pixels).
Quando estava escrevendo o livro, quei na dvida se colocava este captulo no incio,
logo depois de explicar o que uma vievug ou depois como estou fazendo agora. Ao
refletir um pouco, cheguei concluso de que existem assuntos que so delicados
de explicar, e melhor voc ler quando j tiver certo domnio da plataforma.
Este captulo vai lhe tirar muitas dvidas, ou vai deix-lo com muitas dvidas.
Tudo vai depender se voc est pronto para l-lo, ou no. Em minha opinio,
entender o assunto deste captulo um grande diferencial para qualquer desen
volvedor Android.
766
Captulo 30 . Su on d 767
P an odiferentestamanhosdetelas
QPO Descrio (cont.)
Dt (pontos) 1/72 de uma polegada, baseado no tamanho fsico da tela.
dv (density-independent pixels) Essa unidade relativa densidade da
tela. Recomenda-se seu uso para representar tamanhos de largura
e altura das views. O compilador aceita os valores dp e dip'
SD (Scale-independent pixels) Idem ao dp, mas tambm considera o
tamanho da fonte que o usurio est utilizando. recomendado
utilizar essa unidade para especicar o tamanho de uma fonte, para
que ela seia automaticamente ajustada conforme a tela do dispositivo.
eamsung
X ~ '
)1;((;r;1eh1oS Nexus One, Sony Ericsson XPeria XIO, Motorola M11SI011 6
S Galaxy 5 05 quais tm uma tela WVGA que varia entre 480x800px
480 854px I-Ioje em dia este o grupo dos smartphones em geral.
large - T
21bl6t 5de 7 que tm uma tela com aproximadamente 1024x600px.
x 1arge
Tablets
de lo que tm uma tela com aproximadamente 1280x800 px.
E d`das
Alerta: essas ITl 1de tamanhos de tela so aproximadas e podem ter
variaes. ___,_,.---. ~- P S
s
es
. .
como ~ .
.
eccos
Baseado ne
ce u ar - . . Google Android - 4 edio
Nota: ao denir o aspect ratio da tela, comum vermos as notaes que esta tela
de 3:2 ou 4:3. Por exemplo, uma tela HVGA de 32Ox48Opx tem a proporo de
322, enquanto uma tela HVGA de 480x800px tem uma proporo de 4:3.
con eci os
. - ' ' `surgm
lhor_re
e1u1areg gm Android 15 tinham uma tela HVGA de 32Ox48Opx, e alguns dos mais
c h _d - na poca eriam HTC G1 HTC Magic, HTC Hero, Motorola Dext etc.
C _omf
tem
. -do
o, foram
d 16Androi
..
do dispositivos com telas maiores e com me
. recursos para auxi iar o
foram criados
soluao, por 1550 3 Pa - ' ' le funcione perfeitamente em
desenvolvedor a organizar o aplicativo para que e
diversos tamanhos de tel21S
Pt; . -. ~ ,. - .
770 Google Android - 4' edio
tri vocc entender como isso funciona na pratica, vamos criar um novo projeto
l
L1 /res/drawable/ovaI.mI
Feito isso, adicione no arquivo aetivity_main.xml duas imagens, cujo fundo definido
por este shape. A primeira imagem tem 100dp de largura e altura, e a segunda tem 100p.
lr'l'_l /res/Iayout/activity_main.xml
No layout temos duas imagens, uma abaixo da outra. A primeira deniu o tama
nho cm dp (density-inclependent pixels), e a segunda deniu o tamanho em pixels.
A gura 30.1 mostra a pre-visualizaao desse layout no editor do Android Studio
corn a opcao Preview AllsStreen Slzes. Observe que a primeira bolinha (desenhada acimal
esta correta. Porem, a segunda bolinha esta errada, e o seu tamanho sempre vai
variar dependendo da resoluo e densidade da tela do dispositivo.
Esse problema ocorreu por que utilizamos a notao px (pixel) l11\'Ss`tZl1lld' ima
gem. Para soluciona-lo e simples, basta seguir esta regra: Nunca utilize a notaao
px. sempre utilize a notao dp' Portanto, ao conhgurar ambas as imagens com
largura e altura com l0()dp, o resultado sera o mesmo em todos os dispositivos.
i1tdependentemente da resolueao da tela. coulorme mostra a hgura 30.2.
captuh 3 ' 5"P0ndo diferentes tamanhos de telas 771
,_;-.__Y
A nota . . .
30.6 DIP ou DP (density-independent pixel)
'd d de medida de pixel que foi criada para deixa
o dp ou dp uma um a 6
Qlvedor o problema de CXISIII diferentes resoluoes e
transparente para to desenv
densidades de 1612
2 Google
Para
77
voc^Android
_ . .-.4= edio
C entender como a notaao dp funciona, e preciso tomar como base uma
tela HVGA de 320x480 px, com densidade normal. Primeiramente, voc deve achar
eSfranho eu citar estas telas pequenas e ultrapassadas, mas a tela HVGA (320x480)
e considerada a padro (base) da plataforma, portanto, para entender como
funcionam as outras telas, voc precisa entender essa. A tela HVGA (32Ox480) tem
160dp(D0tS Def Inch) e, neste caso, a unidade dp equivalente a um pixel, portanto
100dp igual a 100px.
O problema que essa conta pode mudar conforme a tela do dispositivo. Por
exemplo, pode ser que 100dp seja igual a 75px ou 300px, tudo depende daquela
tal de densidade que comentamos anteriormente.
Mas, na prtica, o que densidade? Ela simplesmente um nmero que deve
ser multiplicado pelo valor em dp, para descobrir o valor em pixels. No caso
da tela HVGA (mdpi) de 320x480px, a densidade 1.0. Portanto, ao fazer a conta
100dp * 1.0 = 100px, ou seja, neste caso 100dp igual a 100px.
j em dispositivos que tm uma tela pequena QVGA (ldpi) de 240x320 pixels, a
densidade da tela 0.75. Se voc zer a conta, 100dp * 0.75 = 75px.
Citando outro exemplo, no caso de uma tela alta densidade NVGA (hdpi) com
480x800px, a densidade 1.5, portanto 100dp * 1.5 = 150px.
Essas foram as trs primeiras densidades que surgiram na plataforma: ldpi (baixa
z ()_75), mdpi (mdia/padro = 1.0) e hdpi (alta = l.5).
Mas claro que voc no vai car fazendo essas contas, pois justamente por isso
foi criada a notao dp.Ao utiliz-la, Android far a correta converso de dp para
pixels utilizando a densidade da tela do dispositivo. Concluindo o raciocnio,
como um dispositivo pode ter uma tela com mais ou menos pixels que outras.
se utilizarmos valores ein pixels, vamos obter resultados diferentes. Por exemplo,
em um dispositivo com tela pequena e baixa densidade, 100px muita coisa C
quase ocupa metade da tela. Mas 100px nos atuais dispositivos, como Nexus 5
ou Nexus , no signica praticamente nada, pois so dispositivos que tm telas
com mais de 2.000px.
,e.~.
Captulo 30 .S Uportando diferentes tamanhos de telas 773
Portanto visu l - -
Ve]a novamente -
Orm 21 mente 100px em um dispositivo com tela pequena bastante coisa,
P . m outro com tela grande de alta dens1dade,100px quase nao seria nada.
bola. Isso a z
100 21 gura 30.1 e repare na segunda bolinha que foi desenhada com
px no Nexus 6. A bolinha mais parece um pequeno pontinho do que uma
contece porque o Nexus 6 contem muitos pixels na tela. A densidade
cou bom e ~
do Nexus 6 4.0, portanto, ao desenhar uma bolinha de 100dp, o Android fez a
conta: 100dp * 4.0 = 400px. Por isso, ao utilizar dp, o resultado visual no Nexus 6
coerente com todos os tamanhos de tela (Figura 302).
E isso que a notao dp faz! Ela traz o mesmo resultado em todos os tipos de
telas. Portanto, lembre-sees a dregra
t : N
unca utilize a notao px, sempre utilize
a notao dp'
Nota: voc j parou para ver o tamanho dos cones de um projeto Android?
O cone da pasta mipmap-mdpi tem 48x48px, mas 0 da pasta mipmap-hdpi tem
72x72px. Note que 48 " 1.5 = 72, onde 1.5 a densidade de uma tela hdpi.
Seguindo o mesmo raciocnio, o cone na pasta mipmap-xxhdpi tem 144x144px
pois 48 ' 3.0 = 144, onde 3.0 a densidade de uma tela xxhdpi.
E para Finalizar agora vai a dica sobre como trabalhar com imagens.
(apt"| 3 ' 5UPortando diferentes tamanhos de telas 775
wa ' - - . .
56 voc no informar
de com ila uma
~ ~imagem
. _ correta para cada densidade em tempo
Nota' se voc tem uma imagem e por algum motivo deseja que ela no seja
oc,._
escalada utilize o qualicador nodp, criando uma pasta /res/dmwable-nodpi.
7
para criar I
N aptulo 7 sobre a classe View, aprendemos a desenhar manualmente no canvas
` views customizadas. Apenas para lembrar, tudo consiste em criar uma
su bclasse de view e sobrescrever 0 metodo onDraw(canvas).
Q htrec
de oCdigo a seguir mostra como desenhar um quadrado de 100x100px.
@0verrde ) {
D
rotected void onDraw(CaVa5 Camas
super.onDraw(CV5)S _ _
canvas . drawRect(0 , Q, 1oo, 100, Dalz
}
O a f
76 Google
7
' . .
Android
mai ` f. . . _ . .-.4_
edio
P , mas muita calma nesta hora! Acabamos de utilizar pixels e violar a regra
s importante que e. Nunca utilize a notaao px, sempre utilize a notao dp`
AO utilizar a notao dp no cdigo do layout XML, o Android faz a correta con
versao de dp para pixels, baseada na densidade da tela do dispositivo. Mas agora
no codigo precisamos recuperar a densidade da tela pela API e fazer o clculo.
Felizmente isso simples, basta adicionar um mtodo como este na sua subclasse
de View:
importante voc entender esse conceito para criar views customizadas que
funcionem corretamente em todos os tamanhos de telas. Por exemplo, comum
utilizar views customizadas com canvas para desenhar um grco de linha, barra
ou pizza. Nesses casos, sempre faa essa converso no cdigo.
Lembre-se de que o segredo do clculo recuperar a densidade do aparelho com
este mtodo:
oat densidade = r.getDisp1ayMetrics().density;
Recomendo que voc crie um projeto de exemplo qualquer emostre o valor des
sa varivel densidade em vrios emuladores diferentes para fortalecer o conceito.
Lembre-se de que a densidade da tela vai retornar sempre valores conforme 8
tabela que vimos no tpico Tabela de densidade dosdispositivos".
nz...
capt"l 30 ' 5UP0rtando diferentes tamanhos de telas 777
30.10 Tamanho da tela em dp
Muitas vezes no degenvglvim
de mma h d ento para Android comum encontrar denies
ue 32Od PS tela ITI CD. Por exemplo, a documentaao ocial do Android diz
Cl I . p o tamanho minimo de uma tela de smartphone, 600dp o tamanho
minimo de um tablet de 7' e 72Odp o tamanho de um tablet de 1O'
Mas como voc sabe o tamanho da tela em dp? 7
Basicamente, o clculo para descobrir o tamanho da tela em dp dividir o tamanho
da tela em pixels pela densidade. Exemplos:
A tela HVGA (32Ox48Opx) ndp tem densidade igual a 1, ento essa uma
tela de 32Ox48Odp.
A tela WVGA (480x800px) hdp tem densidade igual a 1.5, ento essa uma
tela 32Ox533dp.
O Nexus 5 tem uma tela de (1080x1920px) xxhdp com densidade 3.0, ento
essa uma tela 36Ox64Odp.
O Nexus 6 tem uma tela de (144Ox256Opx) xxxhdp com densidade 4.0, ento
essa uma tela 36Ox64Odp.
d__1
/res/values/dimens.xm|
resources> .
public class ConverterPxe1DP1Actvty @te"d5 Activity {
- " o cod1gO
Ao ler cada dimenso 11
f ' lor retorna o automaticamente em pixe s.
0 va
778 Google Android - 4 edio
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// L a dimenso. 0 valor ser retornado em pixels conforme a densidade da tela
t l3f9UFP = getContext().getResources().getDinension(R.dimen.quadrado_width);
oat a1turaPx = getContext().getResources().getDimension(R.dinen.quadrado_height);
// Desenha o quadrado
Ca"Va5-df3WR@Ct(0, G, larguraP, a1turaPx, paint);
}
/res/layout/fragment_main.xmI
<Re1ativeLayout . . .
android:paddingBottom="@dimen/activity_vertica1_margin"
android:paddingLeft="@dimen/activity_horizonta1_margin"
android:paddingRight="@dimen/activity_horizonta1_margin"
android:paddingTop="@dimen/activity_verticai_margin" >
<TextView . . . />
/res/values/dimens.mI
<dimen name="activity_horizonta1_margin">16dp
<dimen name="activity_vertica1_margin">16dp
</resources
As dimenses so utilizadas para especicar espaamentos, tamanhos de fOnI,
tamanhos de view etc. e, dependendo do tamanho da tela, elas podem ser custo
mizadas. Por exemplo, a notao /res/values-w820dp sobrescreve a dimenso para
tablets de 7 ou 1Ocaso estejam na horizontal. O tablet de 7 na horizontal tem
uma tela de aproximadamente 960dp e o tablet de IO tem uma tela de l28Odp.
Bas portando
d ~ ~diferentes
~ tamanhos de telas 779
(|)U|0 30 I Su
Seretigesas
a mffma0S, a notaao /res/values-w820dp indica que esta pasta
. _ . a caso a largura da tela tenha pelo menos 82Odp. O "w" neste caso
518n1Ca Width (largura).
/res/vaIues-w820dp/dimens.xmI
<|'SOU`CS>
<dinen name:"actvty_horizonta1_margn">64dp</dmen>
/res/values/dimens.xm|
_
<dmen name="tet_size">14sp</dnen>
/res/vaIues-xlarge/dimens.xmI
<dmen name="tet_size">40SD</d1e">
/res/vaIues-sw720dp/dimS-Xml
/res/layout-xlarge para tablets de 1O' pois essa notao funciona desde o Android
3_0 e no apenas a partir do Android 3.2.
30.13 . -Links
z rincipais-conceitos
' ` desenvoteisver ap1icativos
sobre como
1
z manhos de te as, _
e' lexocomp
e or isso P 1
Este Caipltulo exphcou Os pl corn resolues e densidades diferentes. Mas o tema
para vamos ta ' recomendo que voc complemente a leitura com textos da
documentao ocial e blog do 6003 6
732 Google Android - 4 edio
Como eu disse na introduo, este captulo iria lhe tirar muitas dvidas, ou deixa
-lo com muitas dvidas. Portanto, caso voc no tenha entendido muito bem algo,
no tem problema, pois o assunto complicado mesmo. Continue lendo o livro e
mais tarde leia novamente esse assunto se precisar. Eu espero que este livro sirva
de guia no para voc somente aprender o bsico sobre o Android, mas para ser
uma fonte de consulta quando quiser relembrar algum conceito.
Mais do que nunca, importante complementar a leitura com a documentao
ocial.
Providing Resources
http://developerandroid.com/guide/topics/resources/providingresources. html
' Supporting Multiple Streens
http://developer android.com/guide/practices/tablets-andhandsets.html
New Tools for Managing Screen Sizes
http://android-developers.blogspot.com.br/2011/07/newtools-for-managing-screcm
-sizes. html
http://android-developers.blogspot.com.br/2014/10/getting-you r-apps-ready-for
-nexus-6-and.html
'\ CAPTULO 31
p o Threads avanado
* j AsyncTask e Loader
z- \ _,_,,(
tagens encapsu _
pequena biblioteca que encapsula a classe AsyncTask, pois, conforme expliquei no
captulo 17, sobre Web services, e vimos no projeto dos carros, tivemos vrias van
lando o cdigo da AsyncTask. Nestes prximos exemplos, utilizarei
diretamente a classe AsyncTask apenas para demonstrar alguns problemas classicos
e assuntos avanados sobre o ciclo de vida da aplicao.
Abra 0 projeto de exemplo deste captulo e procure a activity que demonstra
como fazer o down load de uma imagem. O exemplo de download o mesmo
que fizemos no captulo 10, sobre threads e handler, porm, em vez de utilizar
um ProgressBar para fazer a animao, foi usado um alerta com o ProgressDa1og.
justamente isso nos trar um problm
783
784
Google Android - 4 edio
! DownloadlmagemAsyncTaskActivity.java
if(bitmap == null) {
// Faz o download
downloadImagem(URL);
} else [
// Atualiza a imagem se recuperou o estado
imgview.setImageBitmap(bitmap);
}
@Override
protected Bitmap doInBackground(String... params) {
// Faz o download da imagem
try {
Captulo 31 1 Thfead 5 aVa"ado - AsyncTask e Loader
bmD = Download.downloadBitnap(URL);
} Cth (Exception e) {
Log.e("livroandroid",e.getMessage(), Q);
}
return bitmap;
}
@0verride
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Salva o estado da tela
outState.putParcelable("ing",bitnap);
}
/res/Iayout/activity_downIoad_ima9@m-m|
"utf-8"?>
<?ml version="1.0" encodin9=
<FrameLayout .
<ImageView
android'id="@+id/img" android:layout_width="wrap_content"
android
_ t h 9ht="wrap_content"
: ayo _ '
android 'ayot-gavty-"center" android:scaleType="tCenter" />
786 Google Android - 4 edio
) resultado deste exemplo pode ser visto na gura 31.1, e a princpio tudo esta
funcionando bem.
. V -zaf
l
. _. .ira .
, ..
Google
5
DR|D 1*
w1:w
Aprenda: mu aomxacoes ou: ovsposetos menus ll
f!"\0|f0 SDI .
ii
'WOVQC KYCI1!l.LU
tl
3. Se, depois do download, o usurio girar a tela, a activity vai salvar o Btmap
no mtodo onSaveInstanceState(bundle) e recuperar esse mesmo Btmap depois
quando for recriada_
4. Se durante o download o usurio girar a tela, a aplicao vai travar.
Sobre o ltimo item explicado acima, vamos testar para simular o erro. Execute
o aplicativo e, enquanto o Progressalog est aberto, gire a tela. O resultado um
erro conforme a figura 31.2.
CHPUO 31 I Threads avanado - AsyncTask e Loader 787
No LogCat podemos ver a exceo detalhada (stack trace), conforme a gura 313.
-' )
Ia NQG
Cl Matchgase Regex Wozds gx -0
31
Para solucionar esse p roblema, temos de fechar o ProgressDalog quando a activity for
encerrada mesmo antes de a AsyncTask terminar, conforme demonstrado a seguir:
3
DownloadImagemAsyncTaskAttivity.java
Porm ainda temos um problema, pois, quando a nova activity for criada depois
da rotao da tela, o download da imagem vai iniciar novamente. Neste caso
precisamos dar um jeito de girar a tela sem interromper o download e reutilizar
a primeira thread que est fazendo o download da imagem.
Nota: estou mostrando esses problemas ou situaes porque com certeza isso
algum dia vai acontecer em algum aplicativo seu, e assim voc j estar preparado.
Se precisar, releia este captulo depois, pois estamos dando um passo alm do
tradicional, que mostrar como consultar um web service com uma thread ou
AsyncTask.
/res/Iayout/activity_downIoad_imagem_fragment.xmI
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.con/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent"
android:id="@+id/layoutFrag">
Como podemos ver, a activity no faz nada, pois a lgica ca toda no fragment
DownloadlmagemFragment.java
public class DownloadInagemFragment extends Fragment {
private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
private ProgressDialog progress;
private Bitmap bitmap;
private Imageview imgview;
// A task ca como atributo para car viva durante a rotao da tela
private DownloadTask task;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // Salva 0 estado do ffget
}
@0verride
public View o nCreateView(Layoutlnater inater, @Nullable ViewGroup container,
ie - =
@Nullable Bundle savedInstanceState) l
V' w view - inater inate(R.layout.fragment_download_inagem, container, false);
mgview = (ImageView) view.ndViewById(R.id.ing);
L9-d("livroandroid","frag onCreateView()");
if(bitmap == HU11) {
// Faz o download
downloadImagem(URL);
} else {
// Atualiza a imagem se recuperou o estado
setBitmap(bitmap);
return view;
}
@Override
protected Bitmap doInBackground(String... params) {
Log.d("livroandroid","task doInBackground()");
// Faz o download da imagem
Bitmap bitmap = null;
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
Log.e("livroandroid",e.getMessage(), e);
}
return bitmap;
Captulo 31 u Threads avanado -sync
A Task e Loader
}
@Override
} L0g.d("livroandroid","task onCancelled()");
@Override
public void onDetach() {
super.onDetach();
Log.d("livroandroid","frag onDetach()");
// Fecha o progress antes de desassociar o fragment da activity
closeProgress();
}
});
}
792 Google Android - 4 edio
private void closeProgress() {
Log.d("livroandroid","closeProgress()");
if(progress != null && progress.isShowing()) {
progress.dismiss();
progress = null;
}
Para ajud-lo a estudar e entender o cdigo, recomendo fazer estes dois testes e
vericar os logs do LogCat. O primeiro teste fazer o dovvnload e aguardar at
a imagem ser exibida. Neste caso, teremos os seguintes logs:
D/livroandroid: frag onCreateView() // fragment criado
D/livroandroid > DownloadTask.eecute() // task iniciada
D/livroandroid task onPreEecute() // pr-executa na UI Thread
D/livroandroid showProgress() // mostra o progress dialog
D/livroandroid task doInBackground() // faz o download em background
D/livroandroid task onPostExecute() // m do download na UI Thread
D/livroandroid setBitmap() // atualiza a imagem
D/livroandroid closeProgress() // fecha o progress dialog
O se undanad
' ' -~^SVntTask e Loader 793
(aPtuIo 31 u Threads av
_
O r g 1 o teste e iniciar o download e girar a tela antes de o download terminar
esu tado dos logs devera ser assim:
_ // fragmentum dO
D/livroandroid:
D/livroandroid: frag onCreateView()
> DownloadTask.execute() // task iniciada
D/livroandroid: task onPreExecute() // pr-executa na UI Thread
D/livroandroid: showProgress()
// mostra o progress dialog
D/livroandroid: task doInBackground() // faz o download em background
// Aqui eu girei a tela
D/livroandroid: frag onDetach()
// O fragment ser desassociado da activity
D/livroandroid: closeProgress()
// fecha o progress dialog antes de a activity
// ser destruida
// A activity e o fragment so recriados
D/livroandroid: frag onCreateView() // depois da rotao da tela, vamos recriar
// a view do fragment
D/livroandroid: DownloadTask j est executando. // como o fragment foi retido, vimos
// que o download ainda est executando
D/livroandroid: showProgress() // mostra o progress dialog novamente, pois o
// download ainda est executando
D/livroandroid task onPostExecute() // m do download
D/livroandroid: setBitmap() // atualiza a imagem (vai utilizar a nova
// view do fragment)
D/livroandroid: closeProgress() // feh 0 DFOQFGSS dl09
Neste tpico, voc aprendeu a implementar uma AsyncTask corretamente com um
Fragment e tratar os problemas de troca de orientao da tela. Isso costuma ser um
problema para muitos desenvolvedores Android, mas com este exemplo voc ter
uma boa base para construir seus aplicativos.
. - de forma serial
7I.
31.3 Executando a AsyncTask de forma serial ou paralela
da,todas as tarefas/ tasks executavam
Quando a AsyncTask foi cria
f ~ teInternamen
f ma unica thread.
(uma apos outra) 1T1 U
a AsyncTask controla essa
A .artir do
. foi
d 1,6 esse Androi
la e 0 pool de threads.
alterado
comportamento , _ _que. as
para permitir
P em aralelg No caso de um aplicativo para tablet com diversos
tasl<s executassgldoslela tela isso pode ser til, pois a task de cada fragment
d cutarenlpaf
ffagmems espa 81610 sem depender da resposta de outro. Porem, em aplica
~ is a or 6
PO e exe d m da execuo das tarefas importante, esse comportamento
goes nas qua
pode no ser o ideal.
tP)d~
` - ,. .. ..'
794 Google Android - 4' edio
or isso o Google a partir do Android 3.0 voltou ao comportamento original e
as as tarefas executam em uma unica thread serialmente. Mas pela API pode
mos escolher se dese
Jamos iniciar a AsyncTask de forma serial (padro) ou paralela.
O seguinte codigo mostra como executar a tarefa de forma serial (padro):
AsyncTask task = .._;
task.eecute(parmetros aqu);
// Isso a mesma coisa que...
task.eXeCUte0nEXeCut0r(AsyncTask.SERIAL_EXECUTOR, parmetros aqu);
31.4 Loader
Antes de voc ler sobre loaders, gostaria de ressaltar que este tpico ligeiramente
avanado. Talvez a leitura possa ser um pouco pesada, portanto leia quando achar
necessrio.
Loaders foram criados no Android 3.0 como a forma oficial 'de executar tarefas
assncronas no Android. A interface Loader faz parte do pacote androd.content.Loader.
mas para funcionar em todas as verses do Android podemos utilizara interface
androd.support.v4.content.Loader da biblioteca de compatibilidade v4.
Um loader pode ser utilizado dentro de uma activity ou fragment e utilizad
para executar tarefas assncronas. Uma das suas principais funcionalidade C
monitorar a fonte de dados para que, em caso de mudanas, a interface da tcl
possa ser atualizada rapidamente. Essa funcionalidade no inuito utilizada um
C.
apltuh 31 ' Threads aVa"ado - AsyncTask e Loader 795
da
eressa - .
1vo o . . .
a enda
.
precisamos faz ' ' ~ - .
aPl1CaUV0S que utilizam web ser
V1C5, pois para saber se a fonte de dados mudou
pode ser im er requisioes no servidor Web. Mas existem casos em que isso
me' POI exemplo, Se o seu aplicativo mostra a lista de contatos
mudan
anteri ~ ~ A , . .
P P e ser avisado, para atualizar a lista com as informaes corretas.
Pf1HC1pal vantagem do loader e que ele sobrevive durante uma
` de conguraao do SISICITIQ, como a troca de orientaao. Como vimos
u ormente, podemos utilizar um fragment e rete-lo em memoria para solu~
clonar esse problema com a classe AsyncTask, mas um loader j faz esse gerencia
mento de forma automtica. Caso o usurio gire a tela, por exemplo, o sistema
vai simplesmente reconectar a tarefa no loader que j existe.
Eu particularmente me vejo bem satisfeito com a soluo mostrada anteriormente,
com um fragment retido em memria. No entanto, utilizar loaders a maneira
recomendada pelo Google para executar tarefas assncronas, ento vamos l!
Para utilizar um loader, precisamos implementar a interface Loader. Um loader
responsvel por executar determinada tarefa em segundo plano e retornar o
resultado. O melhor de tudo que o loader tambm pode ser desvinculado da
activity ou fragment.
Como o Loader uma interface, existem duas implementaes nativas padro da
I'a 1 *. , .
plataforma, as classes CursorLoader e AsyncTaskLoader. A classe CursorLoader faz a leitu
ra de um content provider, que utilizado para ler a agenda de contatos, dentre
outras coisas. j a classe AsyncTaskLoader implementa a interface Loader e utiliza uma
AsyncTask internamente para executar a tarefa em background, portanto veja a
importncia de termos estudado a classe AsyncTask. O Loader simplesmente delega
o trabalho para a AsyncTask!
dados ara , , . _
&ca' uma das principais funcionalidades de um loader monitorar a fonte de
ue em caso de mudanas, a interface da tela possa ser atualizada
`da1lf1entl No captulo 32 sobre provedores de conteudo, vamos utilizar a
Neste captulo,
clpsse
vamos f ~ estu _*
a CursorLoader para ler os contatos da agenda e ver essas vantagens na pratica.
_ .~+-- dar os conceitos sobre a API de Loaders.
i__ii_}____ -~ -*__
der
Para executar um 103 fe' 'utilizada a classe
LoaderManager , uma classe
a qual
da com a activity ou o fragment que est executando. O
abstrata que est aSSOC21
mo iniciar um loader:
. . ~ , Hb k);
cdigo a seguir mostra CO
9etLoaderMana9eF()-1-1tLader(1d' args ca ac
O metodo
. ~ llback)
, - der(1d,
tnttLoa
contm os seguintes parmetros:
args, CH
796 Google Android - 4 edio
int id
l
denticador nico para o loader. Se existe apenas um loader, o identi
cador pode ser O (zero).
Bundle args
LoaderCallbacks callback
Imagemloadetjava
public class ImagemLoader extends AsyncTaskLoaderBitmap {
private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
public ImagemLoader(Context context) {
super(context);
}
@0verride
protected void onStartLoading() {
super.onStartLoading();
Log.d("livroandroid", "loader onStartLoading()");
// Precisa chamar o forceLoad() para executar o loader "loadInBackground"
forceLoad();
}
@0verride
public Bitmap loadInBackground() {
Log.d("livroandroid", "loader loadInBackground()");
// Faz o download da imagem
Bitmap bitmap = null;
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
L0g.e("livroandroid",e.getMessage(), e);
}
Captulo 31 1 Thfgads avan d 797
0 - AsyncTask e Loader
.A3
return bitmap;
}
oade
Ao
}
, ,' . .
chama
o loader ` ` ` ~ - ,
1 r f) metodo 9etLoaderManager().1n1tLoader(id,args,ca11back) para iniciar o
r O mewdo "5taftLad19() e chamado. Sua responsabilidade decidir se
iniciado ou nao. Podemos dizer que no metodo onStartLoading() voc
deve dar o comando para iniciar a aret af edverdade, e isso feito chamando o
metodo forceLoad().
Om
todo 1oadInBackground() executado em segundo plano por uma thread e
aqui que voce deve fazer a busca no web service ou banco de dados.
Mas o loader sozinho no faz nada, pois algum precisa cham-lo. Por isso utiliza
mos a classe LoaderManager e o mtodo getLoaderManager() .ntLoader(id,args,ca11back).
Neste caso, o terceiro parmetro caliback deve implementar a interface LoaderManager.
1.
LoaderCa11backs que contm estes trs mtodos:
como
"o..~~
Este mtodo chamado caso o loader seja limpo (feito o reset). Para limpar
um loader, basta cl'1aIT1
d ev
dem
ar o mtodo reset(), o que signica que o loader
e eliminar os dados para economizar recursos e memria.
os dizer que um template bsico para executar um loader seria
Entao P ,d. O demonstrado a seguir. VeJa que a implementao da interface
O CO g ' Q se ks
redoeda
8 chamada. Este callback
LoaderManager.LoaderCa1.1bac __ I . deve
_ criar o
. da execucao no metodo onLoadF1n1shed()_
loader e depois rece ber a resposta
Wi una mgnn {
uam viu awratuiulquthhtu tchau. Unllkc viumn cantar.
lllkble bao sudlnstamsml {
Fins mu = iuhm.ivl|t(l.lga`z~quo_innXui,ingn. totain-. feia);
fff Erich Q inter um manta c un ia existam
viu;
gamiu-nungu-().i|ma|zr\. uu. um u&r(l\hs());
E
Depois des: tenlpiate basico para xuc ~m~nde~r .1 idv:iz` muros msmir un*
exemplo ootrnpktn O obietivu timer 0 :Imm|mad da nnugeut mwamtnl. Ntsk
um vamos mrilimr : mesma ac:i\~ity do exemplo anterior. pois uma zpuur
zherar a dasse do fragmcm. que encapsula toda wa lgica dc d\m1\Iuad da imagem
Lembresr de que wi- pod: abrir o pmjem dv: ~x~mpln dvzsu cpmln no Amhi
Smdio para fadtar aus andas
} nater-"@(R.menu.menu_refresh,nenu);
@0verride
return super.onOptionsItemSelected(item);
}
@0verride
public View onCreateView(LayoutInater inater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_download_imagem, container, false);
imgview = (lmageview) view.ndViewById(R.id.ing);
Log.d("livroandroid","frag onCreateView");
setHasOptionsMenu(true);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d("livroandroid","frag onActivityCreated");
progress();
// Inicializa o loader ou reconecta a um existente
Log.d("livroandroid", "initLoader()");
getLoaderHanager().initLoader(0, null, new LoaderCallbacks());
}
0 ride ,
ltg::SS pr0gressDialog.show(getActivity(), "Aguarde", "Fazendo o download... )
progress.setCancelable(true);
r(new Dialoglnterface.0nCancelListener() {
progress.setOnCancelLiSt@E
});
}
300 Google Android - 4 edio
private void setBitmap(Bitmap imagen) {
Log.d("livroandroid","setBitmap");
// Esconde o progress
closeProgress();
// Atualiza a imagem
imgview.setImageBitmap(inagem);
}
@0verride
public void onLoadFinished(Loader<Bitnap loader, Bitnap data) {
Log.d("livroandroid", onLoadFinished");
setBitmap(data);
}
@0verride
public void onLoaderReset(LoaderBitnap loader) {
Log.d("livroandroid", "onLoaderReset");
}
@Override
public void onDetach() {
super.onDetach();
Log.d("livroandroid", "frag onDetach");
closeProgress();
}
/res/menu/menu_refresh.xm|
<menu xmlnszand
roid="http://schemas.android.com/apk/res/android"
f'\1S=|>r="httpz //schemas.android.com/apk/res -auto"
AI''I-,
. xmlns:tools="http://schemas.android .com/tools" tools:contet=".MainActivity" >
<lte a"dr1d3d="@+id/action_refresh"
androidzt tle="Refresh" android:icon:"@drawable/ic_action_refresh"
app:showAsAction="always" />
Esses logs m ostram a execuo perfeita do loader. Agora faa o seguinte teste.
Depois de carregar a imagem na primeira vez, gire a tela para provocar uma mu
dana nas configuraes do sistema, assim sabemos que a activity e o fragment
Sero destrudos e recriados. Ao girar a tela, as mensagens do LogCat devem ser
como mostradas a Seguf
// 0 fragment ser desassociado da activity
D/livroandroid: frag onDetach() // Depois de girar a tela, cria a view do
D/livroandroid: frag onCreateView() // fragment
// O fragment sabe que o mtodo onCreate() da
D/livroandroid: frag onActivityCreated() // activity terminou
// Reconecta ao loader, pois ele j existe
initLoader()
D/livroandroid: // Recebe o retorno do loader na UI Thread
onLoadFinished()
D/livroandroid: // Atualiza o Imageview
setBitmaD()
D/livroandroid:
802 Google Android - 4 edio
Veja que o interessante deste exemplo que, depois de girar a tela, 30 Chamar O
mtodo initLoader(), o LoaderManager sabe que o loader j retornou os dados e sim
plesmente entrega o resultado no mtodo onLoadFinished(). O interessante que
no foi necessrio salvar o estado manualmente nem reter um fragment.
Outro teste que voc deve fazer girar a tela durante o download. Neste caso, as
seguintes mensagens so impressas no LogCat. Vou comentar apenas as partes
ainda no explicadas.
D/livroandroid? frag onCreateView()
D/livroandroid? frag onActivityCreated()
D/livroandroid? initLoader()
D/livroandroid? onCreateLoader()
D/livroandroid? loader onStartLoading()
D/livroandroid? loader loadInBackground()
// Aqui eu girei a tela, durante o download
D/livroandroid: frag onDetach() // A activity e o fragnent sero destrudos
D/livroandroid: frag onCreateView() // Cria a view do fragnent novamente
D/livroandroid: frag onActivityCreated()
D/livroandroid: initLoader() // Reconecta ao loader, pois ele j existe
// Aguarda a execuo do loader
D/livroandroid: onLoadFinished() // Recebe o retorno do loader na UI Thread
D/livroandroid: setBitmap()
J2
,_
~ ,.
\..J
i Lai' l
lir3'diz
n: *:`y~
tr.\
-W;,, . 1
za '
U,:','-1_
P *-.~ W; ,- l
=z:H='_z.=.:af
1 Goo Ie
z'f1,au<;
Q
11;
zf
ROID
^9"lf|U'I0||9apou1osI|Iw|m6vul1
unolldlil
1 0\/319 namllmm
ImagemLoader.java
public class ImagemLoader extends AsyncTaskLoader {
g ., .
private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
public ImagemLoader(Contet context) {
super(contet);
}
@Override
protected void onStartLoading() f
super.onStartLoadin9(); _
Lo .d("livroandroi d", "loader onStartLoading()");
d ara iniciar o loader ou reconectar a um Ja existente
reci
Ehamiisg hamar 0 forceLoad() para executar o loader "loadInBackground"
forceLoad() 3
}
Google Android - 4 edio
@0verride
protected void onForceLoad() {
super.onForceLoad();
// Chamado ao executar o mtodo forceLoad()
Log.d("livroandroid", "loader onForceLoad()");
}
@0verride
public Bitmap loadInBackground() {
Log.d("livroandroid", "loader loadInBackground()")
// Faz o download da imagem
Bitmap bitmap = null;
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
Log.e(livroandroid",e.getMessage(), e);
1
return bitmap;
}
@0verride
public void deliverResult(Bitmap data) {
// Executa depois do loadInBackground(). Chamado antes de enviar o resultado
// para a UI Thread
Log.d("livroandroid", "loader deliverResult(), isStarted(): " + isStarted());
if(isStarted() && !isReset()) {
super.deliverResult(data);
}
@Override
protected void onStopLoading() {
// Chamado quando o loader ser parado
// Acontece quando o mtodo onStop() chamado na activity ou fragment,
// ou se voc executou o stopLoading()
super.onStopLoading();
Log.d("livroandroid", "loader onStopLoading()");
}
@0verride
protected void onReset() {
super.onReset();
// Chamado caso o loader tenha sido cancelado pelo mtodo reset()
Log.d("livroandroid", "loader onReset()");
}
@Override
(aPU|0 31 I Threads avanado - AsyncTask e Loader 805
Dublic void onCanceled(Bitmap data) {
5UDr.onCanceled(data);
Para voc entender o que signica cada mtodo do ciclo de vida, vamos mais
uma vez mostrar na prtica. Faa o download da imagem e depois pressione a
tecla home do Android. Isso vai deixar a activity e o fragment em background.
Ao clicar na tecla home, a seguinte mensagem deve aparecer no LogCat:
D/livroandroid: loader onStopLoading()
Dessa maneira, podemos ver que o mtodo onStopLoading() est associado com o
ciclo de vida da activity/fragment, e isso muito interessante.
Agora clique no cone da aplicao na tela Home do Android para trazer a activity
novamente para o topo da pilha. Neste caso o mtodo onResune() ser chamado na
activity/ fragment, e as seguintes mensagens devem aparecer no LogCat:
D/livroandroid? loader onStartLoading()
D/livroandroid? loader loadInBackground()
D/livroandroid? loader onForceLoad()
D/livroandroid? loader deliverResult, isStarted(): true
D/livroandroid? onLoadFinished()
D/livroandroid? setBitmap()
eu e *
Ento podemos vericar que quando a activity volta a executar o loader reini
` d oIsso feito automaticamente pelo LoaderManager. Porm aqui se voc perce
ga t- mos um problema uma VZ que 0 mtodo onStartLoading() simplesmente
esta, dandg
man executar o loader porque o mtodo forceLoad() chamado. E por
806 Google Android - 4' edio
isso, conforme os logs, a tarefa do loader executou novamente e uma consulta foi
realizada na internet para buscar a imagem.
@0verride
protected void onStartLoading() {
super.onStartLoading();
Log.d("livroandroid", "loader onStartLoading()");
forceLoad();
l
@0verride
protected void onStartLoading() {
super.onStartLoading();
if(bitnap == null) {
Log.d("livroandroid", "loader onStartLoading() forceLoad()");
// Executa o loader
forceLoad();
} else {
Log.d("livroandroid", "loader onStartLoading() deliverResult()");
// J contm os dados, apenas atualiza a interface l
deliverResult(bitnap);
}
lsso vai resolver o problema de recarregar o loader quando a activity que est em
segundo plano voltar a ocupar o topo da pilha. Portanto, faa 0 teste novamente:
depois do download, clique no boto Home para sair da activity Depois clique
no cone da aplicao para abrir a activity novamente. Desta vez podemos ver as
seguintes mensagens no LogCat:
e 1'
Captulo 31 u Threads avan 307
m
D
ado - AsyncTask e Loader
});
}
d.
Vale ressaltar que existe o mtodo isStarted() do loader, que pode colntrolar Jus
tamente este estado para vericar se o loader est iniciado ou se esta parado.
problema que, mesmo depois de parar o loader, o mtodo deliverlQesult() sera
Chamado quando o mtodo loadInBackground( ) terminar a tarefa. Isso slgnlca que,
mes mo cancelando a tarefa, o resultado ainda entregue para a UI Thread atu
a1'1zar
view a novamente temos de controlar isso manualmente, portanto
Ento
altere este cdigo na classe ImageL0ad@F2
@0verride
public void deliverResult(BtaP data) {
// E xecu a do loadInBackground(). Chamado antes de enviar o resultado
t depois
Lgg,
H " 'LV`O
pgal UI Tgzd.. "loader deliverResult, isStarted(): " + isstrte-d());
iv
if(isStarted() W sneseto) {
L0g.d(|| 1_ roandrod.. "loader super.deliverResult(): " + bitmap);
super.deliverResult(d3ta)5
}
}
808 Google Android - 4= edio
Com isso, antes de entregar o resultado do loader, feita a vericao se ele est
executando com o mtodo sStarted() e tambm vericado se ele no foi inva
lidado com o mtodo sReset(). Depois de adicionar estas linhas de cdigo na
classe Imageloader, teste 0 cancelar no ProgressDialog.
Nesses logs, podemos ver que ao destruir a activity o loader tambm invalidado,
pois o mtodo onReset() foi chamado. Nesse momento, a aplicao deve limpar os
recursos para liberar a memria.
Se for necessario manter o estado de uma thread que est executando durante a
f0ta<a0 da fla, creio que voc consiga resolver isso com um fragment retido em
memoria, conforme explicado neste captulo.
Mas, enm, cabe a voc decidir se utilizar loaders ou no em seus aplicativos.
Neste captulo, estudamos alguns assuntos mais avanados sobre como executar
tarefas em segundo plano e gerenciar o estado da aplicao.
Loaders foram criados para funcionar de forma integrada ao ciclo de vida da
aplicao, porm voc precisa entender o seu funcionamento para tirar total pro
veito e usufruir do potencial dessa API. j no caso da AsyncTask, ela simplesmente
fornece uma biblioteca simples de threads que facilita algumas tarefas, mas voc
deve controlar o estado da aplicao manualmente.
Ambas as APIs funcionam e so muito utilizadas, ento cabe a voc estud-las e
vericar qual vai atender as necessidades do seu projeto. Separei alguns links da
documentao ocial para complementar seus estudos.
Processes and Threads
http.//developer android.com/guide/components/loaders.html
* cAPruLo 32
i Agenda de contatos e
`'. content provider
810
carmos um , . .
(apt"l 32 ' A9enda de contatos e content provider 311
Talvez a melhor forma de entender O que um provedor de contedo seja brin
POUCO com algum provedor de conteudo nativo; por isso, vamos
comear este captulo construind o exemplos para lermos os contatos da agenda.
O Android tem uma srie de provedores de contedos nativos, como por exemplo
consultar os contatos da agenda, visualizar os arquivos, imagens e vdeos dispo
nveis no celular. Independentemente de se esse provedor nativo do Android ou
se criado pelo desenvolvedor, a forma como a aplicao se comunica com um
provedor de contedo padronizada, ou seja, apresenta uma interface padro.
Para ter uma ideia sobre com o que vamos brincar neste captulo, a figura 32.1
mostra o projeto de exemplo He|Io(ontatos executando no emulador. No aplicativo
ser possvel inserir na agenda os contatos Mickey; Pateta e Donald, para depois
list-los na lista. Ao clicar em algum contato, ele ser mostrado com o aplicativo
padro do Android.
_
,`.,.l 1
DonaldI
M ,~4
'Q MlCkEy
, E
.
'z '.
l 11
li
YIY ~
1.
B y
PBGIH
1 q K. 999999999 E
Y
I q Home
A classe androd . net.Uri representa um endereo em geral e neste caso ela identi ca
o endereo utilizado para fazer a consulta no provedor de contedo.
content://media/interna!/images/media
URI que retorna todas as imagens disponveis no celular.
content://media/external/images/media
ContactsContract.Contacts.CONTENT_URI
r='
padro 6 O id do registro. O cdigo a seguir mostra como selecionar o contato de id= 1.
// Equivalente a "content://com.android.contacts/contacts/1"
U i uri ContentUris withAppendedId(ContactsContract.Contacts.CONTENT_URI, 1);
Cursor Q = getContentResolver().query(uri, null, null, null, null);
1 droid provider ContactsContract representa o provedor de COf1fClO da
A C asse an l uma Elaclasse
conteminterna chamada Contacts que dene a
g d' ' ` ara tos
a enda
consulta
de contatos.
Porisponiveis
e as colunas
URI de consulta de conta
exemplo p-
enda de contatos e obter o cursor, podemos ler o nome
depois de consultar a ag
do contato desta format
814 Google Android - 4 edio
Cursor c = . . . ;
String nome = c.getString(c.getColumnInde0rThrow(ContactsContract.Contacts.DISPLAY_NAME));
O cdigo a seguir mostra como fazer um rpido exemplo de como ler os contatos
da agenda, portanto s para aquecermos.
ListaContatosPrintActivity.java
public class ListaContatosPrintActivity extends AppCompatActivity {
private static nal String TAG = "livroandroid";
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_contatos);
printContatos();
}
}
u b . .
con 1 ,, H A _
No cdi o esto
. . .
Captulo 32 . A end d 815
9 a e contatos e content provider
?dresultaddo
El OI` CU 8 deste. exemplo pode ser visto nos logs do LogCat. No caso do emu
1C1OI'l1 I`S COTIEIOS, qu fOI'8ITl lITlpI`SSOS COI'lfOI`ITl O SpI'2lClO.
NF/D(24G): Foram encontrados trs contatos
NF0/1D(240): Nome: Mickey
INFO/ID(240): Nome: Pateta
INFO/ID(240): None: Donald
e,
oe..
utilizamos 0 mtodo query(...), mas existem outros como insert(...), update(...)
e delete( . . .). Na prtica, qu ando formos implementar nossa classe de provedor
~ ~ de contedo.
d contedo vamos implementar todos esses mtodos, pois o ContentResolver faz
a ponte entre a apl1caaO 0 Pmvedor
} nally {
cursor.close();
return fones;
}
return null;
}
Agora que j sabemos como1er o nome do contato, seus telefones e a sua foto
vamos ]untar os conceltos nas classes Contato e Agenda:
Contatojava
public class Contato {
public long id;
public String none;
public Bitnap foto;
public List fones;
// Retorna a URI deste contato, ex.: "contentz//com.android.contacts/contacts/1"
public Uri getUri() {
Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
return uri;
}
// L a foto de un contato
public Bitnap getFoto(Contet context) {
// Cria a URI para o id fornecido
Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
ContentResolver contentResolver = context.getContentResolver();
if (in != null) { _
InputStream in = Contacts.openContactPhotoInputStream(contentResolver, uri);
}
(Fm Agendajava
public class Agenda {
/I content://con.androld.contacts/contacts
private static nal Uri URI = Contacts.CONTENT_URI;
private static nal String TAG = "agenda;
private Context context;
public Agenda(Contet context) {
this.contet = context;
}
} nally {
// Fecha o cursor
cursor.close();
I'tU|'I'l contatos;
}
return null;
.c.nome
=1; '
Captulo 32 1 Agenda de C ontatos e content provider 819
public Ct3t 9etContato(Cursor cu rsor) {
COWGO C = new Contato();
// Id e none
.l0l .dz
St ` z = none; '
C ig 1 _d Cursor-9etL"9(CUFS0r.9etColunnIndex0rThrow(Contacts. ID));
// Fone
return c;
}
} nally {
cursor.close();
}
return fones;
operation.add(
ContentProvider0peration.newlnsert(ContactsContract.RawContacts.CONTENT_URI).
withValue(ContactsContract.Rawtontacts.ACCOUNT_TYPE, null).
withValue(Contactstontract.RawContacts.ACCOUNT_NAME, null).build());
// Nome do contato
operation.add(
ContentProvider0peration.newlnsert(ContactsContract.Data.CONTENT_URI).
withValueBackReference(Contactstontract.Data.RAH_CONTACT_ID, backRefIndex).
withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredNane.CONTENT_ITEH_TYPE).
withValue(ContactsContract.ComnonDataKinds.StructuredName.DISPLAY_NAME,
nome).build());
// Associa o telefone do tipo "Home" ao contato
operation.add(
ContentProvider0peration.newInsert(
ContactsContract.Data.CONTENT_URI).
withValueBackReference(ContactsContract.Data.RAH_CONTACT_ID,backRefIndex).
withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.ConmonDataKinds.Phone.CONTENT_ITEM_TYPE).
withValue(ContactsContract.ComnonDataKinds.Phone.NUHBER, telefone).
withValue(ContactsContract.ConmonDataKinds.Phone.TYPE,
ContactsContract.ConmonDataKinds.Phone.TYPE_HOME).build());
// Foto do contato
Bitmap fotoBitmap = BitmapFactory.decodeResource(context.getResources(), imgFoto);
ByteArray0utputStream stream = new ByteArrayOutputStream();
fotoBitnap.compress(Bitmap.CompressFormat.PNG, 75, stream);
operation.add(
ContentProvider0peration.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAH_CONTACT_ID, backRefIndex)
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(Contactstontract.CommonDataKinds.Photo.PHOT0, stream.toByteArray())
.build());
// Executa a lista de operaes em batch
context.getContentResolver().applyBatch(
ContactsContract.AUTHORITY, operation);
return true;
} catch (Exception e) {
Log.d(TAG, "Erro ao inserir contato: " + e.getMessage(), e);
}
return false;
(I
ap't"l 32 ' ^9enda de contatos e content provider 821
public int delete(Contato c) {
return delete(c.id);
}
u''z-.
public int delete(Long id) {
fl fl C"te"tUS-W1th^Drended1d(c<ntcts.coNrENr_uR1, id);
int count = contet.getCont tR lver().delete(uri,null,null);
en eso
return count;
}
co 1 _ ' ` f ~ - , .
metodos da classe Agenda esto comentados, portanto faa uma boa leitura no
go Utilizar essa classe e simples, o seguinte codigo mostra como ler os contatos:
AQGHB 8 = new Agenda(this); // this = context
List contatos = a.getContatos();
j para ler um contato pelo id, basta este cdigo:
Agenda a = new Agenda(this); // this = context
Contato c = a.getContatoById(id);
}.
public boolean on0ptionsItemSelected(Menultem item) {
if (item.getItemId() == R.id.action_add) {
Agenda a = new Agenda(this);
a.addContato("Mickey","999999999",R.drawable.nickey);
a.addContato("Pateta","888888888",R.drawable.pateta);
a.addContato("Donald","777777777",R.drawable.donald);
Toast.nakeText(this,"Contatos adicionados com sucesso.",Toast.LENGTH_SHORT).show();
return true;
return super.on0ptionsItemSelected(item);
}
nta o - . .
- exemplo
Para melhorar o primeiro p que zemos, o qua p
LO Cat vamos crl
ar um layout com ^ I a fim de mostrar a foto
um Listview
contatos no 8 t ga lista Ao clicar no contato, voce podera fazer algo com ele, mas
e nome do co S disparar uma intent para visualizar os detalhes do contato.
l'1O IIOSSO C215 0 vamo
822 Google Android - 4= edio
Vamos comear pelo layout da activity o qual deve conter o Listview.
/res/layout/activity_Iista__tontatos.xmI
<Re1ativeLayout . . .>
<ListView android:id="@+id/listview"
android:1ayout_width="natch_parent" android:1ayout_height="natch_parent" />
</Re1ativeLayout
Lista(ontatosPrintActivity.java
public class ListaContatosActivity extends AppCompatActivity
inpienents Adapterview.0nItemC1ickListener {
@0verride
protected void onCreate(Bundie savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_1ista_contatos);
nai Listview iistview = (Listview)
ndViewById(br.com.1ivroandroid.contatos.R.id.1istView);
iistview.setOnItemC1ickListener(this);
// Lista os contatos
nal Agenda a = new Agenda(this);
nal List contatos = a.getContatos();
nai ContatoAdapter adapter = new ContatoAdapter(getBaseContext(), contatos);
Iistview.setAdapter(adapter);
}
@0verride
public void onItemCiick(AdapterView<?> parent, View view, int position, long id) {
Agenda a = new Agenda(this);
Contato c = a.getContatoById(id);
Toast.makeTet(this, "Nonez " + c.none, Toast.LENGTH_SHORT).show();
c.show(this); // Mostra o contato disparando uma intent
}
Para o cdigo compilar, precisamos criar o adapter que vai mostrar o contato.
Ci /res/layout/adapter_tontato.xm|
<LinearLayout mins:android="http://schenas.android.com/apk/res/android"
android:iayout_width="match_parent"
android:iayout_height="?android:attr/iistPreferredItemHeight"
android:gravity="center_vertica1" android:orientation="horizonta1">
Captulo 32 I Agenda de contatos e content provider
<QuickContactBadge android:id="@+dmg
- ' I outyh`ht="72d
<TextView "
elg D />
a<1f<1=1av<ut width-"7zp" anrz la
a"drld=d="@+d/tNome" android:layout_marginLeft="4dp"
adrld313V0U_W1dth="wrap_content" android:layout_height="wrap_content"
android:gravty="enter" />
ContatoAdapter.java
public class ContatoAdapter extends BaseAdapter {
private nal Layoutlnater inater;
private Context context;
private List contatos;
public ContatoAdapter(Context context, List contatos) {
this.context = context;
this.contatos = contatos;
this.inater = Layoutlnater.from(context);
}
@Override
public int getCount() {
return contatos != null ? contatos.size() : 0;
}
.d .
@0verride
public Object getItem(int position) {
return contatos.get(position);
}
@Override
public long getItenId(int position) {
Contato c = contatos.get(D0St0);
return c.id;
pu
}
ic i '
@0r1Vew getVew(nt position View convertview, VtewGroup parent) {
tew v = ~ t to, arent, falS); _
V' ew inater inate(br.com.livroandroid.contatos.R.layout.
Tex tView
adapteicon one view
(T;tVew) = ndViewById(br.com.livroandroid.contatos.R.id.tNome)
'
- ' f8dViewById(br.com.livroandroid.
. ' = (QuickContactBadge) vtew.|n
QuickContactBadge 19
contatos.R.id.in9) _ _
Qontato Q = contatos.get(position);
.--Ft
Uri uriContato = C~9efUf1()5
tNome.setText(C-e)5 // Nome
img.assignContactUrt(Uf1c"tat)' /l O O
324 Google Android - 4 edio
Picasso . with(context) . load(uri.Contato) . nto(ing);
return view'|
}
O interessante deste exemplo que, ein vez de utilizar um Inagevew para mostrar
a foto do contato, estamos utilizando a view QuickContactBadge no layout que fa
cilita atribuir a foto do contato por meio de uma URI e ainda mostra o contato
automaticamente ao clicar na foto. Dessa forma, no precisamos nos preocupar
ein como obter o Bitmap da foto do contato, pois o QuckContactBadge faz isso auto
maticamente, com uma ajudazinha da biblioteca Picasso, claro.
i.`
Ao executar o cdigo, o resultado deve ser como a figura 32.2. O lado direito da
gura mostra os detalhes do contato logo depois de seleciona-lo.
E:
A c lrixiti `
i
i lv- i `
, -
. \`.i. tsy
6 .
^
._(..)Q mw LX, .-~_ i
999999999 D
*C
[1 in.
que )
,, isso. e aruim ume
. S C
I _soIvez
CC) '
ap't"| 32 ' ^9enda de contatos e content provider 825
Mas vamos voltar ao problema inicial de t ' '
_ . mo
em mem orla eduma _ pico,
Se voc quiser testar esse cdigo, que vontade. Porm, no gosto de fazer dessa
forma, ento vamos logo ao prximo exemplo. Um dos motivos pelos quais no
gosto de fazer assim porque temos de car mapeando tudo utilizando arrays.
Outra questo que nesse caso no tem como mostrar a foto do contato, pois,
como j estudamos, ela ca em uma tabela diferente da do contato.
Uma soluo simples criar nossa prpria subclasse de CursorAdapter, a qual vamos
chamar de ContatoCursorAdapter.
ContatoCursorAdapter.java
r extends CursorAdapter {
public class ContatoCursorAdapte
public ContatoCursorAd8P@f(C"tet Context' Cursor C) {
5uper(contet, C, @)5
}
ListaContatosPrintActivity.java
public class ListaContatosActivity extends AppConpatActivity inplenents Adapterview.
0nItemClickListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_contatos);
nal Listview listview = (Listview)
ndViewById(br.con.livroandroid.contatos.R.id.listView);
listview.set0nItemClickListener(this);
// Lista os contatos
Agenda a = new Agenda(this);
Cursor cursor = a.getCursorContatos();
nal ContatoCursorAdapter adapter = new ContatoCursorAdapter(this,cursor);
listview.setAdapter(adapter);
}
@0verride
Captu|o32 A ' gend d 827
e contatos e content provider
public void onItenClick '
(AdapterView<?> pa.-em Vie ~ - . . .
}
exe - , . _ ,
} // Mesmo cdigo de antes aqui , W new, mt posltwn' long Id) {
Lista(ontatosPrintActivity.java
imp e
ublic class ListaContatosActivity @
tends AppCompatActivity
@0verride
public void onItemClick(AdapterView<? parent, View view, int position, long id) {
// Igual antes ...
}
@0verride
public Loader<Cursor onCreateLoader(int id, Bundle args) {
// Retorna o CursorLoader para carregar os contatos
Uri uritontatos = ContactsContract.Contacts.CONTENT_URI;
return new CursorLoader(getApplicationContet(),uriContatos,
null,ContactsContract.Contacts.HAS_PHONE_NUMBER +" = 1 ",null,
ContactsContract.Contacts.DISPLAY_NAME);
}
@0verride
public void onLoadFinished(LoaderCursor loader, Cursor cursor) {
// Carrega o adapter com o cursor
adapter.swapCursor(cursor);
1
@0verride
public void onLoaderReset(Loader<Cursor loader) {
// Limpa o adapter
adapter.swapCursor(null);
}
Ao executar esse cdigo, novamente a lista com os contatos ser exibida, mas
desta vez utilizamos loaders, deixando a aplicao mais ecaz.
IS uti
' '-..
Um dos ma iores benefcios da API de L d
ue
ue e oa ers monitorar a fonte de dados para
q m 02150 de mudanas, a interface d a tela possa ser atualizada rapidamente.
Para entend
er esse conceito na prtica si a
na t a g OS SgL1iI'l[S p2ISSOS. Mostre OS contatos
.leonio fizemos anteriormente com qualquer um dos exemplos, menos o
qd 1221 oaders. Clique no contato para visualizar a tela de detalhes, depois
e 1te o contato e exclua-o da agenda. Depois de excluir o contato, o Android vai
voltar para a lista de contatos , porm o contato ainda estar l! Voc precisar
fechar a tela e abri-la novamente para atualizar a lista, ou teramos de imple
mentar algum procedimento de a tualizao (refresh). Algo parecido com o que
zemos no projeto de carros, quando exclumos um carro do banco de dados e
foi necessrio atualizar a lista.
Agora refaa o mesmo teste com esta ltima implementao que utiliza loaders.
Logo depois de excluir o contato e voltar lista, automaticamente a lista estar
atualizada, pois internamente o loader percebeu a mudana e informou ao adapter
que era necessrio atualizar a lista. Tudo acontece de forma transparente e simples.
Enfim, essa uma das principais vantagens da API de Loaders. Para fechar este
tpico e apenas revisando, seguem as principais caractersticas e benefcios de
um loader:
.
1. Encapsula a AsyncTask, portanto executa com uma thread em segundo plano.
2. Facilita a atualizao da view na UI Thread, pois a prpria AsyncTask encap
sula o uso de um handler.
3 Conforme j estudamos, o loader est atrelado ao ciclo de vida da activity
e seus fragments.
4 O loader mantm o estado mesmo durante a troca de conguraes de
sistema, como a troca de orientaao.
.no/ 1- '~'
. Conform acabamos de aprender, um loader consegue monitorar a fonte
de caso
5 em dados para C1Ue
de mudanas ' da tela possa ser
a interface
6.
e nO P1'0J . - ~
atualizada rapidamente.
S ' to dos carros tivssemos um provedor de contedo com um
l der resolveramos de forma simples o problema que tivemos ao excluir
oa , rojeto Na poca tivemos que fazer um articio tecnico para
um carrO 1_ a depgis de excluir um carro do banco de dados. Se voce tem
atualizr prender como um provedor de conteudo e criado, continue
curiosi a 1T1a
lendo o prximo tpico.
33 Google Android - 4 edio
32.10 Criando um provedor de contedo customizado
Ag0ra que ja sabemos o que um provedor de contedo, vamos criar um no
projeto dos carros. O objetivo expor informaes para outras aplicaes, pois
do contrario no vejo muita necessidade de criar um provedor de contedo de
vido a sua complexidade. A no ser que voc queira resolver de forma elegante o
problema 6 da lista do tpico anterior.
Ento, mos obra! Para comear, vamos primeiro revisar a sintaxe de uma URI.
Sabemos que para buscar os contatos da agenda so usadas as seguintes URIs:
' content://comandroid.contacts/contacts/
content://com.android.contacts/contacts/1
@Override
public boolean onCreate() {
return false;
}
@0verride
public Cursor query(Uri uri, String[] projection, String selection,String[]
selectionArgs, String sortOrder) {
return null;
}
@Override
public Uri insert(Uri uri, Contentvalues values) {
return null;
@Override _ _ _
}
O
} 'd , .
public int update(Uri uri, Contentvalues values, String selection,String[]
selectionArgs) {
return 0;
pu ic '
@ glrlt de1et@(Uri uri String selection, String[] selectionArgs) {
return 0;
}
AndroidManifest.xmI
<provider
android:name=".CarroContentProvider"
android:authorities="@string/provider" android:eported="true" />
Para o cdigo compilar, vamos criar o recurso @string/provider; mas, em vez de usar
0 arquivo stringsxml padro, utilize o arquivo strings_cong.xmI, pois assim temos
separadas as strings referentes s conguraes.
(.
aP'tUl 32 I Agenda de contatos e content provider 833
/res/vaIues/strings_cong.xm|
<?ml version="1_@" e ncoding="utf-8"?>
Nota: podemos dizer que agora voc j conhece o quarteto fantstico do Android.
As classes Activity, BroadcastReceiver, Service e ContentProvider so todas cadastradas
no arquivo de manifesto.
car .
deletar o trabalho de persistncia para a classe CarroDB que j existe no projeto dos
ros Lembre se de que no necessariamente o provedor de contedo precisa
P ersistir os dados no SQLite Pois isso no importa'
CarroProvider.java
Ubl glass CarroContentProvider extends ContentProvider {
P // Classe que contm bm de ddS d "
private CarroDB db;
// Colunas para selecionar. . I
Private static HashMaP<5t9* Strmg> C0 unas;
. - - 1 ' t CARRO _ =
Private static nal int CARRO :Oii Z.
Prlvate Staticvalidar
na asI?expressoes
' e ularesF9
sobre a URI
// Utilizada Para
Google Android - 4 edio
@Override
public boolean onCreate() {
// Uri Matcher para fazer as expresses regulares
uriCarro = new UriMatcher(UriMatcher.NO_MATCH);
// content://br.livro.android.provider.carro/carros
uriCarro.addURI(getAuthority(), "carros", CARROS);
// content://br.livro.android.provider.carro/carros/id
uriCarro.addURI(getAuthority(), "carros/#", CARROS_ID);
// Colunas para selecionar
colunas = new HashMap<String, String>();
Capt"| 32 ' Agenda de contatos e content provide;
colunas .put(
Carros._ID, Carros._ID);
colunas. put(
Carros.NOME, Carros.NOME);
colunas. put(
Carros.DESC, Carros.DESC);
colunas. put
[Carros.URL_INFO, Carros.URL_INFO);
colunas .put(
Carros.URL_FOT0, Carros.URL_FOT0);
colunas .put(
Carros.URL_VIDEO, Carros.URL VIDEO);
colunas. put
[Carros.LATITUDE, Carros.LATITUDE);
colunas. put(
Carros.LONGITUDE, Carros.LONGITUDE)
1
colunas .put(
Carros.TIPO, Carros.TIPO);
db = new CarroDB(getContet());
return true;
}
@0verride
// Retorna o MIME type correto
public String getType(Uri uri) {
switch (uriCarro.match(uri)) {
case CARROS:
return Carros.CONTENT_TYPE;
case CARROS_ID:
return Carros.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgunentException("URI desconhecida: " + uri)
}
@Override
public Uri insert(Uri uri, Contentvalues initialvalues) {
// Valida se a URI de /carros
if (uriCarro.natch(uri) != CARROS) {
throw new IllegalArgunentEception("URI desconhecida: " + uri)
}
Contentvalues values;
if (initialvalues != null) {
values = new Contentvalues(initialvalues);
} else {
Values z new ContentValues();
}
long id = db.inset(values);
if (id > 0) {
// Inseriu
Uri urCarr0 = Carro5.getUriId()5
R 01ver().notifyChange(uriCarro, null)
getContet().getContent eS
// Retorn 3 a URI com o id do carro inserido
return uriCarro;
Google Android - 4' edio
@0verride
public Cursor query(Uri uri, String[] projection, String selection, String[]
selectionArgs, String sort0rder) {
// Classe utilitria para criar queries no SQLite
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
if(projection == null) {
projection = new String[] {"_id",Carros.NOME,Carros.DESC,Carros.URL_INFO,
Carros.URL_FOTO,Carros.URL_VIDEO,Carros_LATITUDE,Carros.LONGITUDE,Carros.TIPO};
}
// Ordenao
String orderBy;
if (TetUtils.isEmpty(sort0rder)) {
orderBy = Carros.DEFAULT_SORT_0RDER;
} else {
orderBy = sort0rder;
}
I/ Query
SQLiteDatabase db = this.db.getReadableDatabase();
Cursor c = builder.query(db, projection, selection, selectionArgs, null, null, orderBy);
C
@0verride
Captulo 32 . A
Qenda de contatos e content provide;
public int update(Uri uri, Contentvalues Values, String where, String[] whereArgs) {
int count;
switch (uriCarro.natch( uri)) {
case CARROS:
@0verride
public int delete(Uri uri, String where, String[] whereArgs) {
int count;
switch (uriCarro.natch(uri)) {
case CARROS:
count = db.delete(where, whereArgs);
break;
case CARROS_ID:
// id do carro
String id = ur. getPathSegnents().get(1);
// Atualiza o wh ere se informado com o "_id=?" para atualizar
String whereFina 1 = Carros._ID + "=" + id + (!TextUtils.isEnpty(where)
2 " AND (" + where + ')' 1 "");
count = db.delet e(whereFinal, whereArgs);
break;
default:
mentEception("URI desconhecida: " + uri);
throw new IllegalAF9U
}
1 r().notifyChange(uri, null);
9etContext()-9tC"te"tRe5 ve
return count;
}
}
833 Google Android - 4 edio
O codigo extenso, mas no se assuste. Leia-o com calma e atentamente, pois
basicamente foram implementados os mtodos query(. . . ), insert( . . . ), update( . . _)
e delete( . . .), delegando a lgica para a classe CarroDB.
Os mtodos da classe ContentProvider sempre recebem a URL que em nosso exemplo ser
content://bncomlivroandroid. carros/carros ou content://bxcomlivroandroid.carros/carros/id
caso algum id seja fornecido para selecionar um carro em especco. Conforme
88P
a URI recebida, podemos descobrir se a informao que precisa ser retornada
uma lista de todos os carros ou apenas um carro especfico. Para isso, a classe
android.content.UriHatcher utilizada. Observe que quase todos os mtodos usam
essa classe para vericar o tipo do registro a ser retornado.
Repare que em al uns locais do cdi o referenciada a arte authority da URI,
pois apenas para relembrar a URI composta das seguintes partes:
content://authority/path/id
A parte authority da URI deve ser cadastrada exatamente igual ao atributo
androidzauthorities no arquivo de manifesto.
</span></span> <span class='ocr_word' id='word_1_179' title="bbox 800 1851 974 1893"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">Content</span></span> <span class='ocr_word' id='word_1_180' title="bbox 1005 1849 1202 1893"><span class='xocr_word' id='xword_1_180' title="x_wconf -4">Provider</span></span> <span class='ocr_word' id='word_1_181' title="bbox 1238 1865 1304 1888"><span class='xocr_word' id='xword_1_181' title="x_wconf -1">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_19' title="bbox 672 1933 897 1987"><span class='ocr_word' id='word_1_182' title="bbox 672 1933 897 1987"><span class='xocr_word' id='xword_1_182' title="x_wconf -2"><provider</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_20' title="bbox 764 2017 1667 2062"><span class='ocr_word' id='word_1_183' title="bbox 764 2017 1667 2062"><span class='xocr_word' id='xword_1_183' title="x_wconf -3">android:name=".CarroContentProvider"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_21' title="bbox 763 2099 2651 2154"><span class='ocr_word' id='word_1_184' title="bbox 763 2101 1972 2147"><span class='xocr_word' id='xword_1_184' title="x_wconf -3">android:authorities="br.con.livroandroid.carros"</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2003 2100 2572 2154"><span class='xocr_word' id='xword_1_185' title="x_wconf -2">android:exported="true"</span></span> <span class='ocr_word' id='word_1_186' title="bbox 2606 2099 2651 2153"><span class='xocr_word' id='xword_1_186' title="x_wconf 0">/></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_9' title="bbox 582 2373 1398 2448">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_22' title="bbox 582 2374 1398 2448"><span class='ocr_word' id='word_1_187' title="bbox 582 2379 748 2448"><span class='xocr_word' id='xword_1_187' title="x_wconf -2">32.12</span></span> <span class='ocr_word' id='word_1_188' title="bbox 767 2375 947 2446"><span class='xocr_word' id='xword_1_188' title="x_wconf -2">Classe</span></span> <span class='ocr_word' id='word_1_189' title="bbox 966 2374 1196 2446"><span class='xocr_word' id='xword_1_189' title="x_wconf -3">esttica</span></span> <span class='ocr_word' id='word_1_190' title="bbox 1216 2375 1398 2445"><span class='xocr_word' id='xword_1_190' title="x_wconf -1">Carros</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_10' title="bbox 582 2510 2898 2758">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_23' title="bbox 582 2515 2897 2591"><span class='ocr_word' id='word_1_191' title="bbox 582 2520 719 2591"><span class='xocr_word' id='xword_1_191' title="x_wconf -1">Algo</span></span> <span class='ocr_word' id='word_1_192' title="bbox 751 2525 1081 2590"><span class='xocr_word' id='xword_1_192' title="x_wconf -1">importante</span></span> <span class='ocr_word' id='word_1_193' title="bbox 1111 2518 1266 2572"><span class='xocr_word' id='xword_1_193' title="x_wconf -1">sobre</span></span> <span class='ocr_word' id='word_1_194' title="bbox 1298 2538 1390 2571"><span class='xocr_word' id='xword_1_194' title="x_wconf -1">um</span></span> <span class='ocr_word' id='word_1_195' title="bbox 1422 2517 1680 2586"><span class='xocr_word' id='xword_1_195' title="x_wconf -1">provedor</span></span> <span class='ocr_word' id='word_1_196' title="bbox 1709 2516 1774 2570"><span class='xocr_word' id='xword_1_196' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_197' title="bbox 1805 2516 2074 2570"><span class='xocr_word' id='xword_1_197' title="x_wconf -1">contedo</span></span> <span class='ocr_word' id='word_1_198' title="bbox 2106 2520 2132 2570"><span class='xocr_word' id='xword_1_198' title="x_wconf -1"></span></span> <span class='ocr_word' id='word_1_199' title="bbox 2163 2536 2267 2586"><span class='xocr_word' id='xword_1_199' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_200' title="bbox 2298 2515 2375 2569"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">ele</span></span> <span class='ocr_word' id='word_1_201' title="bbox 2405 2534 2609 2584"><span class='xocr_word' id='xword_1_201' title="x_wconf -2">sempre</span></span> <span class='ocr_word' id='word_1_202' title="bbox 2640 2530 2742 2567"><span class='xocr_word' id='xword_1_202' title="x_wconf -2">tem</span></span> <span class='ocr_word' id='word_1_203' title="bbox 2775 2531 2897 2565"><span class='xocr_word' id='xword_1_203' title="x_wconf -1">uma</span></span></span>
<span class='ocr_line' id='line_1_24' title="bbox 583 2603 2897 2680"><span class='ocr_word' id='word_1_204' title="bbox 583 2612 750 2667"><span class='xocr_word' id='xword_1_204' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_205' title="bbox 776 2616 981 2665"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">interna</span></span> <span class='ocr_word' id='word_1_206' title="bbox 1007 2630 1113 2680"><span class='xocr_word' id='xword_1_206' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_207' title="bbox 1140 2625 1434 2679"><span class='xocr_word' id='xword_1_207' title="x_wconf -1">representa</span></span> <span class='ocr_word' id='word_1_208' title="bbox 1461 2628 1516 2661"><span class='xocr_word' id='xword_1_208' title="x_wconf -1">as</span></span> <span class='ocr_word' id='word_1_209' title="bbox 1541 2607 1762 2661"><span class='xocr_word' id='xword_1_209' title="x_wconf -1">colunas</span></span> <span class='ocr_word' id='word_1_210' title="bbox 1787 2627 1892 2677"><span class='xocr_word' id='xword_1_210' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_211' title="bbox 1916 2627 1972 2661"><span class='xocr_word' id='xword_1_211' title="x_wconf -2">as</span></span> <span class='ocr_word' id='word_1_212' title="bbox 1998 2607 2288 2676"><span class='xocr_word' id='xword_1_212' title="x_wconf -3">aplicaes</span></span> <span class='ocr_word' id='word_1_213' title="bbox 2314 2606 2511 2675"><span class='xocr_word' id='xword_1_213' title="x_wconf -1">podem</span></span> <span class='ocr_word' id='word_1_214' title="bbox 2538 2603 2805 2658"><span class='xocr_word' id='xword_1_214' title="x_wconf -1">consultar</span></span> <span class='ocr_word' id='word_1_215' title="bbox 2827 2621 2897 2655"><span class='xocr_word' id='xword_1_215' title="x_wconf 0">ou</span></span></span>
<span class='ocr_line' id='line_1_25' title="bbox 584 2701 1065 2758"><span class='ocr_word' id='word_1_216' title="bbox 584 2708 776 2758"><span class='xocr_word' id='xword_1_216' title="x_wconf -2">inserir</span></span> <span class='ocr_word' id='word_1_217' title="bbox 796 2722 858 2756"><span class='xocr_word' id='xword_1_217' title="x_wconf -1">os</span></span> <span class='ocr_word' id='word_1_218' title="bbox 880 2701 1065 2755"><span class='xocr_word' id='xword_1_218' title="x_wconf -1">dados.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 582 2817 2899 2994">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_26' title="bbox 585 2818 2898 2888"><span class='ocr_word' id='word_1_219' title="bbox 585 2835 673 2885"><span class='xocr_word' id='xword_1_219' title="x_wconf -2">No</span></span> <span class='ocr_word' id='word_1_220' title="bbox 695 2849 821 2884"><span class='xocr_word' id='xword_1_220' title="x_wconf -1">caso</span></span> <span class='ocr_word' id='word_1_221' title="bbox 842 2828 943 2882"><span class='xocr_word' id='xword_1_221' title="x_wconf -2">dos</span></span> <span class='ocr_word' id='word_1_222' title="bbox 964 2843 1224 2888"><span class='xocr_word' id='xword_1_222' title="x_wconf -1">contatos,</span></span> <span class='ocr_word' id='word_1_223' title="bbox 1244 2842 1352 2879"><span class='xocr_word' id='xword_1_223' title="x_wconf -2">esta</span></span> <span class='ocr_word' id='word_1_224' title="bbox 1374 2824 1538 2878"><span class='xocr_word' id='xword_1_224' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_225' title="bbox 1560 2829 1761 2877"><span class='xocr_word' id='xword_1_225' title="x_wconf -1">interna</span></span> <span class='ocr_word' id='word_1_226' title="bbox 1783 2844 1868 2877"><span class='xocr_word' id='xword_1_226' title="x_wconf -1">era</span></span> <span class='ocr_word' id='word_1_227' title="bbox 1890 2844 1916 2877"><span class='xocr_word' id='xword_1_227' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_228' title="bbox 1938 2832 2609 2882"><span class='xocr_word' id='xword_1_228' title="x_wconf -2">ContactsContract.Contacts,</span></span> <span class='ocr_word' id='word_1_229' title="bbox 2628 2818 2898 2873"><span class='xocr_word' id='xword_1_229' title="x_wconf -2">conforme</span></span></span>
<span class='ocr_line' id='line_1_27' title="bbox 583 2915 1558 2994"><span class='ocr_word' id='word_1_230' title="bbox 583 2922 854 2994"><span class='xocr_word' id='xword_1_230' title="x_wconf -1">podemos</span></span> <span class='ocr_word' id='word_1_231' title="bbox 878 2918 1165 2973"><span class='xocr_word' id='xword_1_231' title="x_wconf -2">relembrar</span></span> <span class='ocr_word' id='word_1_232' title="bbox 1186 2933 1329 2970"><span class='xocr_word' id='xword_1_232' title="x_wconf -1">neste</span></span> <span class='ocr_word' id='word_1_233' title="bbox 1351 2915 1558 2984"><span class='xocr_word' id='xword_1_233' title="x_wconf -1">cdigo:</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 673 3054 2862 3199">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_28' title="bbox 673 3064 1122 3107"><span class='ocr_word' id='word_1_234' title="bbox 673 3064 1122 3107"><span class='xocr_word' id='xword_1_234' title="x_wconf -1">Cursorc=...;</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 675 3127 2862 3198"><span class='ocr_word' id='word_1_235' title="bbox 675 3145 817 3198"><span class='xocr_word' id='xword_1_235' title="x_wconf -3">String</span></span> <span class='ocr_word' id='word_1_236' title="bbox 846 3155 941 3188"><span class='xocr_word' id='xword_1_236' title="x_wconf -3">nome</span></span> <span class='ocr_word' id='word_1_237' title="bbox 968 3159 990 3177"><span class='xocr_word' id='xword_1_237' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_238' title="bbox 1018 3127 2862 3194"><span class='xocr_word' id='xword_1_238' title="x_wconf -3">c.getString(c.getColumnIndex0rThrow(ContactsContract.Contacts.DISPLAY_NAHE));</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_13' title="bbox 583 3246 2898 3426">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 583 3246 2897 3319"><span class='ocr_word' id='word_1_239' title="bbox 583 3269 707 3319"><span class='xocr_word' id='xword_1_239' title="x_wconf -2">Esta</span></span> <span class='ocr_word' id='word_1_240' title="bbox 730 3262 897 3317"><span class='xocr_word' id='xword_1_240' title="x_wconf -2">classe</span></span> <span class='ocr_word' id='word_1_241' title="bbox 920 3264 1124 3313"><span class='xocr_word' id='xword_1_241' title="x_wconf -1">interna</span></span> <span class='ocr_word' id='word_1_242' title="bbox 1147 3256 1276 3310"><span class='xocr_word' id='xword_1_242' title="x_wconf -1">deve</span></span> <span class='ocr_word' id='word_1_243' title="bbox 1297 3274 1378 3308"><span class='xocr_word' id='xword_1_243' title="x_wconf -1">ser</span></span> <span class='ocr_word' id='word_1_244' title="bbox 1399 3273 1523 3306"><span class='xocr_word' id='xword_1_244' title="x_wconf -1">uma</span></span> <span class='ocr_word' id='word_1_245' title="bbox 1547 3252 1816 3306"><span class='xocr_word' id='xword_1_245' title="x_wconf -1">subclasse</span></span> <span class='ocr_word' id='word_1_246' title="bbox 1838 3251 1904 3305"><span class='xocr_word' id='xword_1_246' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_247' title="bbox 1925 3253 2671 3312"><span class='xocr_word' id='xword_1_247' title="x_wconf -3">android.provider.BaseColumns.</span></span> <span class='ocr_word' id='word_1_248' title="bbox 2692 3248 2753 3298"><span class='xocr_word' id='xword_1_248' title="x_wconf -2">Se</span></span> <span class='ocr_word' id='word_1_249' title="bbox 2774 3246 2897 3296"><span class='xocr_word' id='xword_1_249' title="x_wconf -2">voc</span></span></span>
<span class='ocr_line' id='line_1_31' title="bbox 584 3340 2897 3426"><span class='ocr_word' id='word_1_250' title="bbox 584 3374 794 3426"><span class='xocr_word' id='xword_1_250' title="x_wconf -2">reparar</span></span> <span class='ocr_word' id='word_1_251' title="bbox 811 3372 883 3406"><span class='xocr_word' id='xword_1_251' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_252' title="bbox 902 3349 1094 3419"><span class='xocr_word' id='xword_1_252' title="x_wconf -1">cdigo</span></span> <span class='ocr_word' id='word_1_253' title="bbox 1113 3347 1180 3402"><span class='xocr_word' id='xword_1_253' title="x_wconf -1">da</span></span> <span class='ocr_word' id='word_1_254' title="bbox 1199 3345 1363 3400"><span class='xocr_word' id='xword_1_254' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_255' title="bbox 1381 3349 1719 3397"><span class='xocr_word' id='xword_1_255' title="x_wconf -2">CarroProvider</span></span> <span class='ocr_word' id='word_1_256' title="bbox 1738 3346 1899 3395"><span class='xocr_word' id='xword_1_256' title="x_wconf -1">existe</span></span> <span class='ocr_word' id='word_1_257' title="bbox 1917 3361 1943 3394"><span class='xocr_word' id='xword_1_257' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_258' title="bbox 1962 3340 2125 3394"><span class='xocr_word' id='xword_1_258' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_259' title="bbox 2144 3344 2344 3392"><span class='xocr_word' id='xword_1_259' title="x_wconf -2">interna</span></span> <span class='ocr_word' id='word_1_260' title="bbox 2363 3342 2897 3391"><span class='xocr_word' id='xword_1_260' title="x_wconf -2">CarroProvider.Carros.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 674 3473 2179 3716">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 674 3474 2178 3549"><span class='ocr_word' id='word_1_261' title="bbox 674 3492 823 3549"><span class='xocr_word' id='xword_1_261' title="x_wconf -2">public</span></span> <span class='ocr_word' id='word_1_262' title="bbox 854 3490 1001 3535"><span class='xocr_word' id='xword_1_262' title="x_wconf -1">static</span></span> <span class='ocr_word' id='word_1_263' title="bbox 1031 3485 1128 3531"><span class='xocr_word' id='xword_1_263' title="x_wconf -2">nal</span></span> <span class='ocr_word' id='word_1_264' title="bbox 1159 3483 1280 3529"><span class='xocr_word' id='xword_1_264' title="x_wconf -1">class</span></span> <span class='ocr_word' id='word_1_265' title="bbox 1311 3485 1457 3526"><span class='xocr_word' id='xword_1_265' title="x_wconf -2">Carros</span></span> <span class='ocr_word' id='word_1_266' title="bbox 1488 3479 1733 3533"><span class='xocr_word' id='xword_1_266' title="x_wconf -3">implements</span></span> <span class='ocr_word' id='word_1_267' title="bbox 1763 3477 2034 3522"><span class='xocr_word' id='xword_1_267' title="x_wconf -2">BaseColumns</span></span> <span class='ocr_word' id='word_1_268' title="bbox 2067 3474 2083 3529"><span class='xocr_word' id='xword_1_268' title="x_wconf -2">{</span></span> <span class='ocr_word' id='word_1_269' title="bbox 2120 3509 2129 3518"><span class='xocr_word' id='xword_1_269' title="x_wconf -1">.</span></span> <span class='ocr_word' id='word_1_270' title="bbox 2171 3509 2178 3518"><span class='xocr_word' id='xword_1_270' title="x_wconf -2">_</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 765 3563 1767 3631"><span class='ocr_word' id='word_1_271' title="bbox 765 3575 913 3631"><span class='xocr_word' id='xword_1_271' title="x_wconf -2">public</span></span> <span class='ocr_word' id='word_1_272' title="bbox 945 3572 1092 3618"><span class='xocr_word' id='xword_1_272' title="x_wconf -2">static</span></span> <span class='ocr_word' id='word_1_273' title="bbox 1121 3567 1218 3613"><span class='xocr_word' id='xword_1_273' title="x_wconf -2">nal</span></span> <span class='ocr_word' id='word_1_274' title="bbox 1250 3567 1395 3618"><span class='xocr_word' id='xword_1_274' title="x_wconf -2">String</span></span> <span class='ocr_word' id='word_1_275' title="bbox 1425 3567 1522 3607"><span class='xocr_word' id='xword_1_275' title="x_wconf -3">NOME</span></span> <span class='ocr_word' id='word_1_276' title="bbox 1551 3580 1572 3598"><span class='xocr_word' id='xword_1_276' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_277' title="bbox 1605 3563 1767 3613"><span class='xocr_word' id='xword_1_277' title="x_wconf -3">"nome";</span></span></span>
<span class='ocr_line' id='line_1_34' title="bbox 765 3646 1767 3716"><span class='ocr_word' id='word_1_278' title="bbox 765 3660 913 3716"><span class='xocr_word' id='xword_1_278' title="x_wconf -2">public</span></span> <span class='ocr_word' id='word_1_279' title="bbox 945 3657 1092 3702"><span class='xocr_word' id='xword_1_279' title="x_wconf -2">static</span></span> <span class='ocr_word' id='word_1_280' title="bbox 1121 3651 1219 3697"><span class='xocr_word' id='xword_1_280' title="x_wconf -2">nal</span></span> <span class='ocr_word' id='word_1_281' title="bbox 1250 3651 1395 3702"><span class='xocr_word' id='xword_1_281' title="x_wconf -1">String</span></span> <span class='ocr_word' id='word_1_282' title="bbox 1425 3650 1521 3691"><span class='xocr_word' id='xword_1_282' title="x_wconf -1">DESC</span></span> <span class='ocr_word' id='word_1_283' title="bbox 1552 3664 1572 3682"><span class='xocr_word' id='xword_1_283' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_284' title="bbox 1606 3646 1767 3696"><span class='xocr_word' id='xword_1_284' title="x_wconf -2">"desc";</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_15' title="bbox 673 3779 882 3886">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 674 3832 692 3886"><span class='ocr_word' id='word_1_285' title="bbox 674 3832 692 3886"><span class='xocr_word' id='xword_1_285' title="x_wconf -2">}</span></span></span>
</p>
</div>
</div>
</body>
</html>
vtd . , .
a l` - .
(aPtU| 32 I Agenda de contatos e content provider 839
para cada
er.Carros carro.
A classe CarroPro
Ob 1 d bfI p
exem 1 arro, porpossibilita acessar as co unas a ta e a dis oniveis
geturdu 'd Serve tambem que a classe CarroProvtder.Carros dene o mtodo
"9 1 ) L1Sado para construir uma URI informando o id do c
p O content://bx com.lz1/roandroid.carros/carros/id.
Concluindo, com as cla
sses CarroProvder e CarroProvtder.Carros, podemos consultar
ista de carros do content provider, conforme demonstrado a seguir:
// Busca os carros do nosso content provider: content: //br.com.lvroandroid.carros/carros
Cursor cursor = getContentResolver().query(CarroProvder.Carros.CONTENT_URI, null,
null, null, null);
// L os carros do cursor
List carros = new ArrayLst();
while (cursor.moveToNext()) {
Carro c = new Carro();
c.nome = cursor.getStrng(cursor.getColumnInde(Carros.NOME));
c.desc = cursor.getStrng(cursor.getColumnInde(Carros.DESC));
carros.add(c);
}
Nao
Apen
as ara au ~ ,
informaes para outra aplicacao.
~ -farei
lvimenro aqui,
esse desenvo
projeto,Pcriei
pois este um timo exerccio para voce.
xili lo deixei pronto o projeto de exemplo (arros(ontentProvider. Nesse
ta da lis
carros consultando o provedor de conteudo, da mesma
forma que zem os com o exemplo da agenda de contatos. Poderamos at con
ara inserir
tinuar o desenvolvimento P 'editar e excluir carros, mas isso ca como
exerccio para VOCC
32.13 Lili: teis
Conforme estudamos ncsu: apimlqcaso seja mamrio acpor as inormab do
nosso aplicativo para outras aplicaes, podemos criar um pmvcdor de conlcdo
O cxcmplo mais famoso dc provedor dc contedo a agcnda de contatos pois
CSS: aplicao pcnmrc quc qualquer outra faa a leitura em sua basc dc dados.
Lcmbnc-sc dc que. caso voc no precise expor as inormacs do scu aplicativo
recomenda-se dcix-las privadas Ou sqn. sucienu: utilizar um bancodc dados
com SQLite, ou qualquer outro modclo dc
Para complementar seus estudos, scpnrci alguns links da documcnu.\ ocial.
II! !! - Cld Hilda!
http;//dcvclopcmndroizicom/gide/tpics/providcrs/crm:n-prcmdnlIlu|
- uma nau; - uma quzywu a anmzu
utpx//dcvdopmndrdamdUairng/lmd-dam-bakgmmd/kmploadrzhnJ
KL cAPTuLo 33
\
_,__.,.__,/' S
\- 4
O Android permite enviar e receber uma mensagem SMS pelo aplicativo, e isso
pode ser utilizado para fazer com que aplicaes se comuniquem.
Embora o mundo mobile esteja cada vez mais conectado e dependente da conexo
com a internet, s vezes enviar uma mensagem de SMS pode ser uma boa soluo,
pois no depende de conexo e existe a garantia de entrega da mensagem.
Neste captulo, aprenderemos com exemplos prticos como enviar e receber
mensagens por SMS.
AndroidManifest.xmI
<manifest ..
> ndrod ' name="android . permission _ READ_CONTACTS" />
<uses-permsson a
841
842
Google Android - 4' edio
Poclenios eiiviar uina niensageni SMS por intent ou pela APL A classe IntentUtils
da biblioteca android-utils contm um mtodo para enviar o SMS por intem.
to IntentUtiIs.java
public class IntentUtils [
public static void sendSms(Context context, String fone,String msg) {
Uri uri = Uri.parse("sms:"+fone);
Intent intent = new Intent(Intent.ACTION_SENDTO,uri);
intent.putExtra("sms_body", msg);
context.startActivity(intent);
1
Para enviar o SMS pela API, crie a classe Sms com o seguinte cdigo-fonte:
ii Sms.java
public class Sms {
private static nal String TAG = "livro";
// Envia um SMS para o nmero indicado
public void send(Context context, String to, String msg) {
try {
Smsanager smsanager = SmsManager.getDefault();
Pendinglntent plntent = Pendinglntent.getBroadcast(context, 6, new Intent(), 0);
smsanager.sendTextMessage(to, null, msg, plntent, null)
Log.d(TAG,"Sms.send to["+to+"] msg["+msg+"]");
} catch (Exception e) {
Log.e(TAG, "Erro ao enviar o SMS: " + e.getMessage(),e);
}
lhnawzquewnumocodgopmaenwmwnnSMSpormnmtoupdaAPLwmum
alterar o exemplo que mostra a lista de contatos, para que, ao selecionar um
contauxsepiexndoinnrxnnnconiesmuduasopes
A lista de opes ser criada como um array em XML.
Captulo 33 | SMS 843
/res/values/strings.xmI
<I`SOU`CS>
/res/values/arrays.xmI
<?xnl version="1.0" encoding="utf-8"?>
<array nane="popup_sns">
@string/action_sns_intent
@string/action_sms_api
Esse array pode ser inflado para String[], sendo assim melhor denir esses re
cursos em XML, pois facilitam a manuteno e a internacionalizao.
Com o objetivo de inar o array e mostrar um popup com as opes para o
usurio escolher, vamos utilizar um AlertDialog. Siga em frente e altere o mtodo
onItenClick() que trata o evento de seleo da lista conforme demonstrado a seguir:
ListaContatosFragment.java
public class ListaContatosFragment extends Fragnent . . . {
@0verride
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Agenda a = new Agenda(getActivity());
nal Contato c = a.getContatoById(id);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("SMS para: " + c.n0ne);
builder.setItems(R.array.popup_sms,new Dialoglnterface.0nClickListener() {
@0verride
public void onClick(DialogInterface dialog, int which) {
String fone = c.fones.get(0);
if(which == 0) { // envia SMS por intent
IntentUtils.sendSms(getActivity(), fone, "Ol "+c.nome+", tudo bem?");
} else { // envia SMS pela API
Sms sms = new Sms();
844 Google Android - 4 edio
sns.send(getActivi.ty(),fone,"0I "+c.nome+", tudo ben?");
Toast.rfiakeTet(getActvty(), "SMS enviado para: " + c.nome,
Toast . LENGTH_SHORT) . show( );
}
});
AlertDia1og dialog = bu1der.create();
dia1og.show();
}
77' l
}
Feito isso, execute 0 projeto no emulador e clique em algum contato. A gura 33.1
~ l
mostra o alerta com as opes.
~ v . `ll
'rf i
ir
'3M
, A l*f.\"vi
( ..vi ;
,.Mickey
_
V
G .az
Mickey , Donald
*_f,_ _
-, A
1 "\
' _ Pateta
l; A
FOlmf' zztudo4bemll
Mickey,
AndroidManifest.xmI
<manifest
[ SMSReceiver.java
public class SHSReceiver extends BroadcastReceiver {
private static nal String TAG = "livroandroid";
@Override
public void onReceive(Contet context, Intent intent) {
L9-d(TAG, ">" + intent.getAction());
Sms sms = new Sms();
// L a mensagem
Smsessage msg = sms.read(intent);
String celular = msg.getDisplay0riginatingAddress();
String mensagem = msg.getDisplayMessageBody();
Log.d(TAG, "SHSReceiver: sms[" + celular + "] -> " + mensagem);
Noticationtil.create(contet,1, new Intent(contet,MainActivity.class),
R.mipmap.ic_launcher,"Novo SMS de: " + celular,mensagem)
}
Sms.java
public class Sms {
`9tUI`I'l NSQS
}
Captulo 33 n SMS 847
Este cdigo l a mensagem SMS que est no formato PDU (Protocol Description
Unit) e retorna um array de objetos do tipo SmsMessage, o qual contm o remetente
e o texto da mensagem recebida.
Com o objetivo de testar se o broadcast receiver est corretamente configurado
para interceptar a mensagem SMS, podemos simular o envio de um SMS para o
emulador. Para isso, abra a ferramenta Tools > Android > Android Device Monitor, selecione
o emulador na janela Devices e abra a janela Emulator Control. Por m, digite o nmero
do telefone fictcio no campo lncoming Number, selecione o item SMS e escreva algum
texto. Quando tudo estiver pronto, clique no boto Send, conforme podemos ver
na gura 33.3.
Q Android Device Monitor - 5
File Edit Run Window Help
Ti vs l l l < lllli ^ j
Devices
.tl
sf.z._
x _. _,_ ,P:'
s . ,~.I-az)
1 ? JJ =
lquick Aczi _ _~ v v sb [5 Qi v
s, Ll$>_T_h':;_i__@.l`S9l._@. 'ri ' Emul" E ` :X$t:_f-.
Telephony Status
_ ,.
> V. 'Send
" ' Han
V~a
com.android.phone . (9 SMS _ fg________H_____NH__________~_*_________
com.google.process.loc , MSSQE l Opa tudo bem?
com.androd.desl<clock L
com.google.process.ga
com.google.android.gn " ~
j A ru of566M H
Isso vai simular o envio de um SMS. Sendo assim, caso o broadcast receiver esteja
corretamente congurado, a aplicao vai interceptar a mensagem SMS_RECEIVED e
vai imprimir no LogCat o remetente e o texto da mensagem.
D/lvroandrodz SMSRecever: sms[987654321] -> Opa, tudo bem?
A figura 33.4 mostra que existem dois cones de notificao no emulador indi
cando que a mensagem foi recebida. O primeiro cone da aplicao nativa de
mensagens e o segundo cone da nossa aplicao. Como a mensagem enviada
por broadcast, todos os receivers recebem a mensagem, portanto no tem como
evitar que a aplicao nativa tambm a receba. Ao abrir a noticao (lado direito
da figura), podemos ver as duas notificaes, sendo que criei a noticao do
livro seguindo o mesmo modelo da notificao nativa. O ttulo o nmero do
remetente, e o contedo do texto a mensagem recebida por SMS.
7
848 Google Android - 4 edio
ve. 5 ' u 2 18
. '`.L,_;
^ - _ * -4 ~I z
. 'I> .z7..r..z,,_
\
.)r,T;:
; `_~_ _. _.,$ .:Vz_;
*
r _ -; ifg \ ;=.;*z, .i'
* V 'I z Q" x~,
.,\z;
5. 'Ifzzm
z~-.^' ~__z__,
JZ '- "L;.*' ,
.X .
*fm
987654321 . .
_ Qe7s4a2i
W731
Neste captulo, estudamos como enviar e receber mensagens SMS. Seguem alguns
links para continuar seus estudos.
API Reference - Classe BroadcastRecever
http://det/el oper: and mid. com/ refereme/cmd roid/corztent/Broadcas Reeei irei: html
http://developer:android.com/reererzce/andrnd/telephony/SmsMessuge.html
CAPTULO 34
Gestos
\_q
1.
.KI.
\\ ,<`^
34.1 Introduo
O reconhecimento de gestos pode ser realizado de duas maneiras:
A primeira maneira comparar o gesto efetuado pelo usurio corn algum gesto
previamente cadastrado. Esse o caso de uma assinatura digital, na qual o usurio
faz o cadastro da assinatura, a m de repetir o gesto para validar a assinatura.
AAPI permite comparar se o gesto feito pelo usurio segue o mesmo padro da
assinatura cadastrada e retorna uma pontuao (escore), informando se o gesto
parecido com o padro salvo. Baseado na pontuao, a aplicao deve decidir
se o gesto vlido ou no.
A segunda maneira de reconhecer gestos monitorar o deslocamento nos eixos
x e yr para reconhecer gestos bem populares, como o swipe lateral e zoom.
Ac executar 3 aplieaeo pela primeira vez, voce ver uma tela vazia, conforme a
figura 34.1. Nesta tela voc podera desenhar um gesto, porm esse gesto s ser
reconhecido caso ele esteja cadastrado na biblioteca de gestos.
849
85 Google Android - 4 edio
R score 12 l50?O13QZ5lS30'I ~
Para cadastrar um gesto, clique no boto (+) na action bar. Isso vai abrir a tela para
cadastrar um gesto, conforme a figura 34.2. Nessa tela, desenhe o gesto, digite um
nome qualquer para ele e clique no boto Salvar. A gura 34.2 mostra um gesto
com a letra R sendo salva na biblioteca.
R
l SALVAR
Quando voc j tiver cadastrado algum gesto em arquivo, volte tela inicial e re
pita o mesmo gesto. O lado direito da figura 34.1 mostra o gesto da letra R sendo
reconhecido com uma pontuao maior que 5.
Captulo 34 1 Gestos 851
A rea que voc pode desenhar o gesto delimitada pela view Gesture0ver1ayVew, a
qual foi inserida no centro do layout. Essa view sozinha faz o desenho e permite
reconhecer o gesto. Depois de reconhecer um gesto, ele precisa ser validado. Isso
feito com a classe GestureLbrary, que permite carregar a biblioteca de gestos que
foi salva em arquivo. Se o gesto for reconhecido como vlido pela biblioteca,
mostrada a pontuao (escore) do gesto no Textview. Quanto mais alta for a pon
tuao, melhor, pois signica que o gesto desenhado prximo do cadastrado.
No nosso exemplo, estou aceitando somente gestos com pontuao superior a 5,
pois a aplicao que dene qual pontuao mnima deseja aceitar.
Dica: na tela de cadastrar o gesto, voc deve desenhar o gesto, digitar um nome
para ele e clicar no boto Salvar. Foi o que z na gura 34.2. Somente gestos
previamente cadastrados podem ser identicados. Depois de cadastrar o gesto,
volte na tela inicial e tente repeti-lo. Se a pontuao (escore) for alta, o gesto
ser identicado.
Agora que j sabemos o que queremos fazer, vamos colocar a mo na massa. Crie
um projeto chamado HeIIoGestureLibrary e adicione a permisso wRITE_EXTERNAL_STORAGE
no arquivo de manifesto, pois vamos salvar os gestos feitos pelo usurio no
SD card. Para reconhecer um gesto no aplicativo, deve-se utilizar a classe
androd.gesture.Gesture0ver1ayVew, que uma view especial responsvel por moni
torar os gestos. Portanto, vamos criar o seguinte layout:
/res/layout/activity_main.xml
<?m1 version="1.0" encodng="utf-8"?>
<LinearLayout xmlns:androd="http://schemas.android.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:Iayout_heght="match_parent"
androd:orientaton="vertca1" android:paddng="16dp">
<TetView android:id="@+d/text"
androd:1ayout_width="match_parent" androd:1ayout_height="wrap_content" />
<FrameLayout androd:d="@+d/layout" androd:1ayout_weght="1"
android:1ayout_width="match_parent" androd:Iayout_heght="0dp" >
<ImageVew androd:d="@+d/img"
androd:1ayout_wdth="wrap_content" android:1ayout_height="wrap_content"
androd:1ayout_gravty="CtF"/>
android.gesture.Gesture0ver1ayView android:d="@+d/gesturevew"
android:1ayout_wdth="match_parent" android:1ayout_height="match_parent"
androd:gestureCo1or="#00ff00" />
352 eoogie :mmia-4=z|
Utilizei um FraneLayout para colocar o Gestureverlayview por cima do Inageview, assim
logo depois de fazer o gesto possvel extrair o Bitmap do desenho com o objetivo
de mostr-lo no Inageview.
GestureLibrary gestureLib = . . .,
@Override
}
public void onGesturePerformed(Gesture0verlayView overlay, Gesture gesture) {
// Faz a biblioteca de gestos reconhecer o movimento
ArrayList predictions = gestureLib.recognize(gesture);
Isso til caso voc queira embutir no aplicativo o arquivo previamente criado
com a biblioteca de gestos. Mas geralmente os gestos sero salvos no SD card pelo
aplicativo em tempo de execuo. Neste caso podemos utilizar um cdigo como
este para ler a biblioteca e carregar o arquivo:
File le = new File(Environment.getEternalStorageDirectory(), "gestures");
gestureLib = GestureLibraries.fronFile(file);
boolean ok = gestureLib.load();
Ser exatamente isso que vamos fazer no nosso exemplo. Para finalizar o exemplo,
vamos mostrar o cdigo da activity que carrega a biblioteca de gestos no mtodo
onResume() e faz o reconhecimento dos gestos.
853
Captulo 34 u Gesto:
r; ManActIvIty.]ava
00verrtde
protected vold onResume() {
super.onResume();
/I L o arqulvo de gestos do SD card
re|dGe|ture|();
l
00verrlde
publlc vold onGesturePerformed(Gesture0verlayVlew overlay, Gesture gesture) {
tf(gestureLlb == null) return;
// Faz a blbltoteca de gestos reconhecer o movlmento
Arr|yLt|tPredlctlon predlctlons n gesturetlb.recogntze(ge|ture);
Predlctlon maScore = null;
for (Predlctlon predlctlon : predlctlons) {
// Vamos acettar somente escores matores que S
lf (predlctton.|core S.0) {
lf (maxScore == null || maxScore.score < predlctlon.score) {
maScore = predlctlon;
}
@0verride
public boolean onCreate0ptionsHenu(Menu menu) {
getMenuInater().inate(R.menu.menu_main, menu);
return true;
}
@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_add) {
startActivity(new Intent(this, SalvarGestoActivity.class));
return true;
1
return super.on0ptionsItemSelected(item);
}
) /res/menu/menu_man.xmI
<menu mlns:androd="http://schemas.androd.com/apk/res/androd"
m1ns:too1s="http://schemas.androd.com/tools"
xm1ns:app="http://schemas.androd.com/apk/res-auto" >
<item android:id="@+d/action_add" androd:title:"@strng/acton_add"
androd:con="@androdzdrawable/c_menu_add" app:showAsActon="a1ways" />
/res/layout/attivity_saIvar_gesto.mI
<?xm1 version="1.G" encodng="utf-8"?>
<LnearLayout m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:layout_wdth="match_parent" android:1ayout_heght="match_parent"
androd:orentaton="vertca1" androd:paddng="16dp" >
<EdtText
androd:d="@+d/text"
androd:1ayout_width="match_parent" android:layout_heght="wrap_content" />
<Butt0n
androd:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center_horizonta1" android:text="@string/salvar"
androd:onC1ck="onC1ckSa1var"/>
<FrameLayout android:d="@+d/layout"
androd:1ayout_width="match_parent" androd:1ayout_heght="0dp"
androd:layout_weght="1" >
<ImageVew androd:d="@+d/img"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center"/>
<androd.gesture.Gestureverlayview androd:id="@+d/gesturevew"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:gestureCo1or="#00ff00" />
</FrameLayout
</LnearLayout>
Esse layout tambm reconhece o gesto com a classe Gesture0ver1ayVew, mas logo
depois que o gesto reconhecido ele permite digitar o nome do gesto e clicar no
boto Salvar para salv-lo na biblioteca de gestos em arquivo.
856
Google Android - 4' edio
Sa|varGestoActivity.java
@0verride
public void onGesturePerformed(Gestureverlayview overlay, Gesture gesture) {
// Mostra o gesto em um Imageview
int w = (int) gesture.getBoundingBo().width();
int h = (int) gesture.getBoundingBox().height();
nal Bitmap b = gesture.toBitmap(w, h, 8, Color.GREEN);
img.setImageBitmap(b);
overlay.setGesture(gesture);
this.gesture = gesture;
}
public void onClickSalvar(View view) {
String nome = text.getTet().toString();
if(gesture != null && nome != null) {
// Salva o gesto
gesturelib.addGesture(nome, gesture);
gestureLib.save();
nal String path = le.getAbsolutePath();
Toast.makeTet(this, "Gesto salvo: " + nome + ", Dth " + Dth,
Toast.LENGTH_SHORT).show();
}
Captulo 34 n Gestos . _. ,. ,f-asta
'r
No caso do emulador, o arquivo com a biblioteca de gestos e salvo na p
857
..3
for detectado, conforme a lista a seguir:
abstract boolean onF1ng(MotionEvent el, MotonEvent e2, oat ve1octyX, oat ve1ocityY)
Chamado quando um movimento de swipe lateral foi encerrado, informan
do a coordenada inicial e a nal, assim como a velocidade do movimento
nos eixos x e y
SwipeActivity.java
@0verride
public boolean onTouchEvent(MotionEvent event) {
// Aqui est o segredo. Delega o tratamento do touch para a classe Gesturebetector
boolean tratouEvento = gestureDetector.onTouchEvent(event);
if(tratouEvento) {
return tratouEvento;
}
859
Captulo 34 n Gestos
return super.onTouchEvent(event);
} catch (Exception e) { }
return false;
}
/res/layout/activity_swipe.xmI
<FrameLayout mlns:android="http://schemas.android.com/apk/res/android"
mlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:background="#eeeeee" android:padding="16dp" >
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:src="@drawable/android" />
<TetView
android:id="@+id/text" android:layout_gravity="center"
86 Google Android - 4 edio
androd:1ayout_wi.dth="wrap_content" android:1ayout_heght="wrap_content"
android:tet="@string/faca_um_gesto" androd:tetSze="18sp" />
/res/values/strings.xm|
<l'eSOU rces>
Esse arquivo de layout tem uma imagem no topo e um Textvew no centro, o qual
vamos utilizar para exibir as informaes sobre o gesto. Por exemplo, se o usurio
mover o dedo da esquerda para a direita, vamos mostrar essa informao no Textvew.
Para o cdigo compilar, adicione a biblioteca Nine0IdAndroids no arquivo app/buildgradle.
app/buiId.gradIe
Compile 'com.nineoldandroids:1brary:2.4.0'
OOO
.
..)z;,'
2%
Com essa biblioteca, animar uma view (mantendo a compatibilidade) simples assim:
annate(i.ng).xBy(-100); // Move 100p para esquerda
/res/layout/activity_zoom.xml
<FrameLayout mlns:android="http://schemas.android.con/apk/res/android"
xmlns:tools="http://schemas.android.con/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="16dp" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:text:"@string/faca_um_gesto" />
br.com.livroandroid.gestos.HyInageView
android:id="@+id/img"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" />
MyImageView.java
package br.com.livroandroid.gestos;
Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Desenha um quadrado apenas para visualizar a view
canvas.drawRect(0, 0, getHidth() - 1, getHeight() - 1, paint);
// Multiplica a largura e a altura pela escala
int larguraComEscala = (int) (imgw * scaleFactor);
int alturaComEscala = (int) (ingH * scaleFactor);
// Posiciona o desenho no centro da tela
int dx = getwidth() / 2 - larguraConEscala / 2;
int dy = getHeight() / 2 - alturaComEscala / 2;
canvas.translate(dx, dy);
// Faz a escala do desenho (para dar zoom out/in)
canvas.scale(scaleFactor, scaleFactor);
// Desenha a imagem
drawable.draw(canvas);
}
}
854 Google Android - 4' edio
O cdigo dessa classe est criando um Drawable com a imagem /res/drawable/android. jpg
para depois no mtodo onDraw(canvas) desenhar a imagem.
O cdigo faz um clculo para posicionar a imagem no centro da tela, algo que
bem comum em desenhos de baixo nvel em canvas. Depois que descobrimos as
posies x e y do centro da tela, o mtodo canvas.translate(d,dy) chamado para
informar ao canvas que o desenho deve ser feito nessas coordenadas.
Repare tambm que temos a chamada do mtodo canvas.scale(scaleX,scaleY), que
faz o desenho ser escalado conforme a proporo informada para os eixos x e
y Por exemplo, se voc escalar a imagem com o valor 1, signica que a imagem
car com o tamanho original, j 0,5 corresponde metade do tamanho, e 2, ao
dobro do tamanho original.
Note que a escala da imagem congurada pelo atributo scaleFactor, o qual por
padro tem o valor 1. Nossa brincadeira ser fazer a activity alterar o valor dessa
escala, conforme o gesto de zoom do usurio, refletindo no tamanho da imagem
a m de obter os efeitos de zoom out e zoom in.
ZoomActivity.java
@0verride
public boolean onTouchEvent(MotionEvent event) {
// Delega o evento de touch ao detector de gestos
865
Captulo 34 I Gestos
boolean ok = gestureetector.onTouchEvent(event);
if(ok) {
return super.onTouchEvent(event);
}
return ok;
}
// Detecta o zoom
class ZoomGestureDetector extends ScaleGestureDetector.Sinple0nScaleGesturetistener {
@0verride
public boolean onScale(ScaleGestureDetector detector) {
// Armazena o fator de escala detectado
scaleFactor *= detector.getScaleFactor();
// Exibe o fator de escala no Textview
tet.setTet(String.value0f(scaleFactor));
// Altera o fator de escala da view customizada e pede para ela se redesenhar
img.setScaleFactor(scaleFactor);
img.invalidate();
return true;
}
@0verride
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@0verride
public void onScaleEnd(ScaleGestureDetector detector) {
}
: . V '
t-Vf$.?.t- ,_ `
z apy . :
1""!'
Tambm vamos estudar a plataforma do Google Fit que faz parte do Google Play
Services.
867
Google Android - 4= edio
Para demonstrar como listar os sensores disponveis no celular, vamos criar uma
activity como esta. Caso prera, abra o projeto HeIIoSensores no Android Studio e
acompanhe a explicao.
ListaSensoresAttivity.java
public class ListaSensoresActivity extends AppConpatActivity inpiements Adapterview.
0nItemCiickListener {
private Sensoranager sensoranager;
private ListSensor sensorList;
@0verride
37 Google Android-4-ediao
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_lista_sensores);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
sensoranager = (Sensoranager) getSystenService(SENSOR_SERVICE);
sensorlist = sensoranager.getSensorList(Sensor.TYPE_ALL);
List<String nomes = new ArrayList<>();
for (Sensor s : sensorlist) {
nomes.add(s.getName() + " - " + s.getVendor() + " - + 5,getType());
}
@0verride
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Sensor sensor = sensorList.get(position);
String msg = sensor.getName() + " - " + sensor.getVendor();
Toast . makeTet( this , msg , Toast. LENGTH_SHORT) . show( ) ;
}
r? /res/layout/attivity_Iista_sensores.xmI
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout _ _ .>
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:layout_height="match_parent" /
Nota: para testar esses exemplos, use um aparelho com Android, pois o emulador
no consegue simular os sensores. A lista de sensores disponveis vai variar de
acordo com cada dispositivo, pois eles esto fortemente atrelados ao hardware.
Quanto mais novo o aparelho. mais sensores ele tera.
871
Captulo 35 I Sensores e Google Fit
M
y \/Ll Il '
~/w l/"'*'F .
E.*' ,fzi fl
:a-s
Y l
,1;i 1 H l
^" l 11i T 1 11 iMl.) l'1Fl ll
Mp
hk
_ _ " ` 'I
Jzr L V ( 18 .ill JiMrx1 lJ
P A
Avz;Q_
1 ( Im (ill il
Al-`U*$"1'\ i Qi ( K/ H ufifl Qll 71
nullja
ListaSensoresActivity.java
}
372 Google Android - 4 edio
Observe que no possvel passar o objeto Sensor como parmetro, pois ele no
do tipo Serializable nem do tipo Parcelable, portanto estou passando o ndice
selecionado na lista. Na outra activity teremos de consultar a lista de sensores
novamente e utilizar este ndice para obter o sensor selecionado.
Agora crie a nova activity com o wizard NewActivity> BIankActivity do Android Studio
e deixe o cdigo-fonte conforme demonstrado a seguir:
TestSensorActivity.java
public class TestSensorActivity extends AppConpatActivity inplenents SensorEventListener {
private Textview tvalor;
private Sensoranager sensoranager;
private Sensor sensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_sensor);
// Posio do sensor
int position = getIntent().getIntExtra("position",G);
// Busca o sensor pela posio
sensorManager = (Sensoranager) getSystenService(SENSOR_SERVICE);
ListSensor> sensorList = sensoranager.getSensorList(Sensor.TYPE_ALL);
sensor = sensorList.get(position);
tValor = (Textview) ndViewById(R.id.tValor);
// Mostra o none e fabricante do sensor
getSupportActionBar().setTitle(sensor.getNane());
getSupportActionBar().setDisplayHoneAsUpEnabled(true);
}
@Override
protected void onResune() {
super.onResune();
// Registra o sensor selecionado
sensoranager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORHAL);
}
@0verride
protected void onPause() {
super.onPause();
// Cancela o registro do sensor
sensoranager.unregisterListener(this);
}
873
Captulo 35 n Sensores e Google Fit
@0verride
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Chamado se mudou o status de preciso do sensor
}
@0verride
public void onSensorChanged(SensorEvent event) {
// L os valores do sensor
StringBuffer sb = new StringBuffer();
for (int i = 0; i < event.values.length; i++) {
sb.append(i).append(": ").append(event.values[i]).append("\n");
}
// Mostra os valores
tvalor.setTet(sb.toString());
}
/res/Iayout/activity_test_sensor.xmI
<?nl version="1.0" encoding="utf-8"?>
<LinearLayout ... android:orientation="vertical" >
<TetView
android:id="@+id/tvalor" android:tet="16sp"
android:layout_width="wrap_content" android:layout_height="wrap_content" /
@0verride
protected void onPause() {
super.onPause();
// Cancela o registro do sensor
sensorManager.unregisterListener(this);
}
Para testar este exemplo, execute o projeto em um dispositivo real, depois clique
z 1 ~
em algum sensor da lista. A figura 353 mostra o resultado do sensor de aceler
metro. Veja que no array existem trs posies, que so referentes aos eixos x, y
e z do acelermetro.
Q V 3 23:27
,Tp v . ue. , .fz _, p `'*,
?f<fFe"`iefffeeaatetetttefei A tfff`
' u 0198312207
MPU651-5Acceler0fe1ef~lnvenSen9-1 : a |ua>o
z o ni; sua
_...
API. No nal do captulo faremos um exemplo com a Fitness API.
., . tfzz
Rotation Vector - QTI - 11
W! . .. V . v 123.26
0 54 0
f S=e;o==~ori-19 _ 3
Embora o exemplo anterior j tenha sido suciente para mostrar como listar todos
os sensores c ainda obter o valor de cada sensor, vamos explorar alguns sensores
individuais apenas para praticar.
O sensor de luminosidade representado pela constante Sensor.TYPE_LIGHT e infor
ma a intensidade da luz e claridade no ambiente externo. Nesse sensor, para ler
o valor da intensidade da luz, basta acessar a primeira posio do array em que
o nmero oat representa o valor obtido do sensor.
public void onSensorChanged(SensorEvent event) {
// L o valor do sensor
oat valor = event.values[0];
}
i /res/layout/activity_sensor_seekbar.xmI
<LinearLayout . . . android:orientation="vertical">
<TetView android:id="@+id/tvalor"
android:layout_width="wrap_content" android2layout_height="wrap_content" />
<SeekBar android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="ll_parent" android:layout_height="wrap_content"
android:padding="2Gdp"
android:progress="0" android:nax="100" />
Agora, vamos escrever o cdigo da activity que vai utilizar o sensor do tipo lumi
nosidade com a constante Sensor.TYPE_LIGHT. Veja que estamos obtendo o sensor
com o mtodo sensoriianager.getDefaultSensor(tipo); dessa maneira, possvel obter o
sensor padro caso exista mais de um. De qualquer forma, importante validar se
o sensor existe, pois, como j foi explicado, tudo depende do hardware do aparelho.
f) LuminosidadeActivity.java
@0verride
protected void onResume() {
super.onResume();
if (sensor != null) {
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
@0verride
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Mudou o status de preciso do sensor
}
@0verride
public void onSensorChanged(SensorEvent event) {
// L o valor do sensor (intensidade da luz)
oat valor = event.values[6];
((TextView) ndViewById(R.id.tValor)).setText("Luz: " + valor);
// Atualiza o valor no SeekBar
progress.setProgress((int) valor);
}
}
878 Google Android - 4 edio
Caso voc tenha um aparelho com o sensor de luminosidade, brinque com os
resultados testando o sensor em ambientes com bastante luz ou escuros. Voc
vera que a SeekBar vai mudar constantemente, pois est medindo a intensidade
da luz no local.
l l _. i A. "'_:
direto no sol, ento note que a SeekBar est totalmente preenchida.
.,_ . _ . _. ._.. x , ,, ._ ,. ,_ .
l f __ z. z ;.;, 2 . ',`.;_j.:zI ,ff .. _ .-Z* y *'" _ gs'
. _ . , ~ , lo da
celular do ouvido, ele detecta a proximidade e desliga o LCD 21 C ,P f mos
mizar recursos durante a ligaao. Quando terminamos de conversar, ao
o celular do ouvido, automaticamente o LCD da tela ligado.
Para utilizar esse sensor, o procedimento e exatamente igual ao do exemp
luminosidade ou temperatura, e o sensor retorna ap enas um valor, que 3 dis
tncia em centmetros do objeto que est prximo do celular. Portanto, para ler
esse valor, podemos utilizar o seguinte cdigo:
public void onSensorChanged(SensorEvent event) {
// Distncia em centimetros
oat valor = event.va1ues[6];
}
I
Z
/res/layout/activity_sensor_aceIerometro.xmI
<LinearLayout . . . android:orientation="vertical" >
<TextView android:id="@+id/tX" android:tet="@string/"
android:layout_width="wrap_content" android:layout_height=wrap_content" />
<TetView android:id="@+id/tY" android:tet="@string/y"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TetView android:id="@+id/tZ" android:tet="@string/z"
android:layout_width="wrap_content" android:layout_height="wrap_content" /
Esse layout tem trs Textviews para exibir os valores dos eixos x, y e z. j o cdigo
da activity funciona da mesma forma que os outros sensores. Basta ler os dados
do sensor do tipo Sensor.TYPE_ACCELEROMETER:
AceIerometroActivity.java
public class AceleronetroActivity extends AppConpatActivity inplenents SensorEventListener {
private static nal int TIPO_SENSOR = Sensor.TYPE_ACCELEROHETER;
Para testar o acelermetro, vamos travar a orientao da activity na vertical. Faa isso
adicionando o atributo androd:screen0rentation="portrait" ao arquivo de manifesto.
lsso necessrio porque, caso a orientao do celular mude para a horizontal, os
valores do acelermetro niudaro tambm. Logo explicarei mais detalhes sobre isso.
<activity androd:name=".Ace1erometroActvity" android:screen0rentation="portrait" />
"l z'
L '* \;\;\'
` ^tWY*5 1 x Missas
i
l
,:.
l
j,__ z X
'~>~ z z~-
l* `__._""""'*--_..._=_
I
A
r* k
Nessa gu ra, podemos ver claramente que, ao girar o celular, o sistema de coorde
nadas x, y e z no muda, portanto no reflete a posio atual do aparelho. Nesse
caso, o que faremos compensar essa rotao, pois possvel identicar em qual
posio o celular se encontra e trocar os valores dos eixos x e y retornados pelo
acelermetro. Para descobrir a orientao atual do dispositivo, podemos utilizar
a classe Display e o mtodo getRotation():
HindowManager windowrlanager = (windowanager) getSystemService(wINDON_SERVICE);
Display display = windowlianager.getDefaultDisplay();
883
Captulo 35 1 Sensores e Google Fit
SensorUtiI.java
// Troca os valores de e y
oat[] values = new oat[3];
values[0] = sensorX;
values[1] = sensorY;
values[2] = sensorZ;
return values;
}
AcelerometroAttivity.java
public class AcelerometroActivity extends AppCompatActivity implements SensorEventListener {
private long time;
@Override
public void onSensorChanged(SensorEvent event) {
885
Captulo 35 I Sensores e Google Fit
Nesse exemplo, para efeito de testes, foi inserido mais um Textvew com o iden
ticador @+d/tMsg no arquivo de layout, para mostrarmos o retorno do mtodo
SensorUtil.getRotatonString(context).
/res/Iayout/activity_sensor_ateIerometro.xmI
I'',
<LinearLayout . . . androd:orientation="vertlcal" >
<TextVew androd:id="@+id/tX" . . . />
<TetVew android:id="@+id/tY" . . . />
<TetVew android:id="@+d/tZ" . . . />
TextVew android:d="@+id/tsg"
android:layout_width="wrap_content" androd:layout_heght="wrap_content" />
BonecoAndroidView.java
package br.con.livroandroid.sensores;
@0verride
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
887
Captulo 35 u Sensores e Google Fit
paint.setColor(Color.LTGRAY);
// Desenha o fundo da view (um quadrado cinza)
canvas.drawRect(0, 0, getHidth(), getHeight(), paint);
paint.setTextSize(toPixels(16));
paint.setColor(Color.BLACK);
canvas.drawText("x/y:" + d+"/"+dy,50,50,Dt);
// Desenha a imagem das posies x e y
canvas.translate(dx, dy);
drawable.draw(canvas);
}
AcelerometroJogoActivity.java
@0verride
public void onSensorChanged(SensorEvent event) {
super.onSensorChanged(event);
// L os valores retornados pelo acelermetro
oat values[] = SensorUtil.xAceleronetro(this, event);
oat sensorX = values[0];
888 Google Android - 4 edio
oat sensorY = values[1];
// Vai incrementando os valores de x e y para o objeto se mover
int newdx = androidView.getDx() + (int) sensorX * 10;
int newdy = androidView.getDy() + (int) sensorY * 16;
int imgw = androidView.getDrawable().getIntrinsicNidth();
int imgH = androidView.getDrawable().getIntrinsicHeight();
// No deixa o valor car negativo ou maior que o tamanho da tela
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
if ( (ewdx < 0 II newdx + imgw > displayMetrics.widthPiels)) {
/I Atualiza o eixo x
androidview.setDx(newdx);
}
// Redesenha a view
androidview.invalidate();
1
No layout da activity vamos colocar um Textview para mostrar os valores dos eixos
x e y. Tambm vamos adicionar a view customizada que vai desenhar o boneco
do Android.
@ /res/layout/actvty_aceIerometro_jogo.xmI
<LinearLayout . . . android:orientation="vertical">
<TextView android:id="@+id/tX"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="@string/x" android:tetSize="18sp" />
<TextView android:id="@+id/tY"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="@string/y" android:tetSize="18sp" />
<br.com.livroandroid.sensores.BonecoAndroidView android:id="@+id/bonecoview"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="9" />
Ao executar esse exemplo, voc poder mover o bonequinho do Android pela tela
como se estivesse jogando. A Figura 35.9 demonstra o resultado quando o boneco
est praticamente no centro da tela. A implementao e simples. Estamos mo
nitorando o sensor do acelermetro e atualizando a propriedade dx e dy da view
889
. . . . - ` o.
. ' 'No la Out
.~'1
(aptulo 35 I Sensores e Google Fit
nhado um
customizada. Com isso a view vai desenhar a imagem I1553 P05' y
mostramos os valores de x e y do acelerometro. Dentro da view e dese d
quadrado cinza e a imagem do Android que fica se movendo. No top0 5qUer (
do quadrado, desenhada a posio ldx
o boneco
e dy do
naAndroid
qua I foi
_
desenhado. Acredito que se voc executar o exemplo no emulador cara m2lS
simples de entender; portanto, faa isso. Note que este exemplo s1mpleS. HIQ
tratou aspectos mais avanados como aceleraao e gravidade, TT1215 0 0bJUV O
apenas brincar um pouco.
roozms
,, . . . , '
os dados dos sensores e ainda salvar ou ler os dados na nuvem do Google. In
clusive existe o aplicativo Google Fit para celulares, tablets e wearables. Tambm
existe o site https://t.google.com/ para visualizarmos os dados salvos pelo aplicativo
padro do Google Fit.
O melhor de tudo que essas APIs esto disponveis para o desenvolvedor' logo QSQ
voce esteja desenvolvendo aplicativos para a area de sade que precisam utilizar Q
contador de passos e batimentos cardacos, as APIs do Google Fit podem ajud lo
O Google Fit faz parte do Google Play Services, ento para utilizar esses servios
precisamos nos conectar aos servidores do Google, conforme aprendemo s no
89 Google Android -4' edio
captulo 23, sobre localizao e GPS. Isso feito porque os dados dos sensores
podem car salvos na nuvem do Google, conforme ilustrado na gura 35.10.
f/F `\z~~
\i I.
Google
FIDGSS SOTG .
"""" `\1__,//F
Android Device
i"
p Android App
..:_,j.1Ir~
toogle ily s.>rwt~s 5
Android Sensors
Wearable Devices
' it,
facilitando persistir todos os dados e consult-los na nuvem do Google,
Um aspecto negativo que para o Google Fit funcionar necessria uma conexo
com a internet para pelo menos se conectar ao Google Play Services Depois que
o aplicativo fez a conexo pela primeira vez, ele pode trabalhar de forma offline
mas de qualquer forma necessria essa conexo inicial Utilizando 0 Goog|e F =
892 Google Android - 4' edio
os dados sero salvos na nuvem do Google, e depois com a HstoryApi possvel
inclusive consultar esses dados.
Caso seja um requisito do seu projeto que o aplicativo deva funcionar totalmente
offline, no possvel utilizar o Google Fit. Nesse caso deve-se utilizar o sensor
do tipo TYPE_STEP_COUNTER, conforme estudamos neste captulo. A desvantagem desta
soluo que se perde o poder de toda a plataforma do Google Fit.
Mas chega de conversa! Vamos criar um exemplo de aplicativo que utiliza o Google
Fit para criar um pedmetro.
Primeiramente, declare a dependncia do Google Play Services:
app/buiId.gradIe
compiie 'com.goog1e.android.gms:play-services:7.0.0'
Fo GoogIeFitPedometroAttivity.java
@Override
protected void onStart() {
super.onStart();
// Conecta ao Google Play Services
if(!nGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) {
toast("mGoogleApiClient.connect()");
nGoogleApiClient.connect();
}
@0verride
protected void onDestroy() {
super.onDestroy();
// Desconecta ao sair
mGoogleApiClient.disconnect();
}
@Override
public void onConnected(Bundle bundle) {
toast("Conectado ao Google Play Services!");
startPedoneter();
}
});
}
};
// Contador de passos (TYPE_STEP_COUNT_DELTA)
SensorRequest req = new SensorRequest.Builder()
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setSanplingRate(1, TineUnit.SECONDS)
.build();
I/ Ativa a API do Fitness
PendingResult result = Fitness.Sensorshpi.add(mGoogleApiClient, req, listener)
Log.d("livroandroid","Google Fit pedometer ativado: " + result);
}
@0verride
public void onConnectionSuspended(int cause) [
toast("Coneo interronpida.");
}
@0verride
public void onConnectionFailed(ConnectionResult result) {
if (!result.hasResolution()) {
// Se for algum erro de conguraco ou servio, mostra alerta
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),this, G).show();
return;
}
@0verride
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_OAUTH) {
if (resultCode == RESULT_OK) {
// Depois que o usurio autorizou, faz login no Google Play Services
if (!nGoogleApiClient.isConnecting() && !nGoogleApiClient.isConnected()) {
nGoogleApiClient.connect();
}
/res/layout/attivity_google_t_pedometro.xmI
<LnearLayout ... androd:orientatlon="vertca1">
<TetView android:text="@strtng/qtde_passos"
androd:1ayout_wtdth="wrap_content androd:1ayout_hetght="wrap_content />
Textvew androld:d="Q+ld/text"
android:l.ayout_utdth="wrap_content" androd:1ayout_heght="wrap_C0tt" />
/LtnearLayout>
iii /res/values/strings.xmI
<`SOUl'CS>
Q
I ""' II
Para o cdigo funcionar, necessrio habilitar a Fitness API na pgina de console
do Google Developers (https://console.developersgoogle.com). Faa isso no menu
APIs&autl1>APls. Lembre-se de que j zemos isso nos captulos 22 e 28, sobre mapas
e GCM push. A gura 35.11 mostra estas trs APIs habilitadas na minha conta.
,- -__ ... ___
Some
their
API
APIs are enabled automatically.
services. I
Fitness API
Google Cloud Messaging for Android;
Qoogle Maps Anafgia API vz
I
Na janela que vai abrir (Figura 35.12), selecione o item lnstalled application e o tipo de
aplicao Android. Digite o nome do pacote do aplicativo e o SHA1 Fingerprint
do certicado. Lembre-se de que voc aprendeu a obter o SHA1 Fingerprint no
captulo 22, sobre mapas. Feito isso, clique no boto (reateClientID. O legal dessa chave
que ela fica associada sua conta do Google, porm no preciso fazer nada no
aplicativo. Basta criar essa congurao no servidor do Google, e est tudo pronto.
Create Client ID
Application type
Web application
Accaased by web browsers over n netwonr
Service account
Calls Google APis on behall of your application instead of an end-user Learn more
O installed application
Runs on a desktop computer or handheld dente (like Androd or Phone)
AP! rcquests are Sent dlrectly to Google lrom your clients' Android devices
Google verllles that each request orlglnates lrom an Android application that
matches the package name and SHA1 slgnmg CI2l1i|C\ lmgorprmt name listed
below.
Package namo
br_com.Iivroandroidsensores
Deep linking
Enabled
O Disabled
Uma vez que a configurao e a chave 0Auth foram criadas, vamos executar o aplica
tivo. Na primeira vez que voc acess-lo, ser solicitado que o usurio faa o login
com alguma conta do Google, conforme a figura 35.13. Isso acontece justamente
porque foi criada uma chave de autenticao do tipo 0Auth.
897
Captulo 35 I Sensores e Google Fit
.:_ _
mmol . ,
*OWWV
1...
(1
.:-_
.._.,
Quantidade de Passos i
Passos: '18
_ _. __. _.________
"*f- -_ 1r
https://developerandroid.com/reference/com/google/android/gms/tness/Fitness.html
E
~1\ cAPtuLo 36
J Bluetooth
\
"i
Nota: para testar os exemplos deste captulo, recomendo ter dois dispositivos
Android, pois vamos enviar mensagens de um para 0ur0_
90 Google Android ~ 4 edio
36.2 Ativando o Bluetooth por programao
Com o objeto BluetoothAdapter em mos, o prximo passo veri car se o Bluetooth
est ativado no dispositivo. Isso feito com o mtodo isEnabled(), que retorna true
caso o Bluetooth esteja ligado. Caso no esteja, podemos solicitar ao usurio para
ativar as conhguraes do Bluetoth.
if (btfAdapter.isEnabled()) { // Bluetooth est ligado }
else { // precisa ligar o Bluetooth }
AndroidManifest.xml
<manifest . . _
<uses-permission android:nane="android.pernission.BLUETO0TH_ADHIN" /
<uses-permission android:nane="android.permission.BLUETO0TH" /
<application . . . />
lffil BIuetoothCheckActivity.java
J'r
rwJ"
Q I_
..,
. ~. "H ,'~
\ .I
.._. \.' ~_.Q;;;_-z, )
'1kJf)":?\_(:\.1'-.y (."1t(-Udh {
Fzmrzol Az" 1
t . ' l..Uu1() 1) BL1lt_'[:'[_
36.3 Listando os dispositivos pareados
Uma funcionalidade bem comum na comunicao por Bluetooth a busca de
dispositivos j pareados. Se voc no est acostumado com Bluetooth, o pare
amento necessrio antes de trocar informaes entre um dispositivo e outro,
sendo feito geralmente na primeira vez que esses dispositivos estabelecem uma
conexo, informando um tipo de senha que ambos precisam aceitar. Depois que
os dispositivos esto pareados, eles podem trocar informaes.
Para recuperar a lista de dispositivos pareados, utilize o mtodo getBondedDevices():
Set pareados = btfAdapter.getBondedDevices();
for (Biuetoothevice device : lista) {
String nome = device.getName();
String address = device.getAddress();
// Fazer algo com essa informao...
}
ListaDevicesActivity.java
@0verride
protected void onResume() {
super.onResume(); // Garante que no existe outra busca sendo realizada
if (btfAdapter.isDiscovering()) { btfAdapter.cancelDiscovery(); }
// Dispara a busca
btfAdapter.startDiscovery();
dialog = ProgressDialog.show(this, "Exemplo", "Buscando dispositivos Bluetooth...",
false, true);
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
// Iniciou a busca
count = 0;
Toast.makeTet(contet, "Busca iniciada.", Toast.LENGTH_SHORT).show();
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// Terminou a busca
Toast.makeTet(contet, "Busca nalizada. " + count +
" devices encontrados", Toast.LENGTH_LONG).show();
dialog.dismiss();
// Atualiza o listview. Agora vai ter todos os devices pareados,
// mais os novos que foram encontrados
updateLista();
}
};
@0verride
protected void onDestroy() {
super.onDestroy();
// Garante que a busca cancelada ao sair
if (btfAdapter != null) {
btfAdapter.cancelDiscovery();
1
@Override
public void onItemClick(AdapterView<?> adapterview, View view, int idx, long id) {
// Recupera o device selecionado
BluetoothDevice device = lista.get(idx);
String msg = device.getName() + " - " + device.getAddress();
Toast.makeTet(this, msg, Toast.LENGTH_SHORT).show();
}
O arquivo de layout dessa activity contm apenas um Listview para mostrar a lista
de dispositivos j pareados e aqueles que sero encontrados pela busca.
/res/layout/activity_Iista_devices.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout. . . android:orientation="vertical" >
<ListView android:id="@+id/listview" android:layout_weight="1"
android:layout_width="match_parent" android:layout_height="0dp" />
Durante a busca, foi encontrado um Nexus que tambm foi adicionado na lista.
Quando a busca finalizada, a mensagem com ao ACTION_DISCOVERY_FINISHED
enviada e interceptada pelo receiver. Nesse momento, foi mostrado um toast com
a quantidade de dispositivos encontrados, que no meu caso foram trs.
l
11
preciso deixar um dispositivo visivel por programa210, Pdems d _t_vO
intent, a qual recebe como parametro o tempo em segund05 que O P
car visvel para a busca:
907
d.
Essa intent solicita ao usurio que permita que seu dispositivo seja encontrad
por outros durante o tempo em segundos informado. Ao dispar-la, o An r0l
vai mostrar um alerta na janela conforme a gura 363. Se o usuario aceitar a
solicitao, esse dispositivo poder ser encontrado na busca.
Dica: caso voc tenha executado o exemplo anterior e seu dispositivo no foi
encontrado na busca, deixe-o visvel e repita a busca.
V'l,-1; z
Para iniciar o Bluetooth no modo servidor, a primeira tarefa criar um objeto UUID.
O importante utilizar este mesmo UUID no aplicativo cliente para fazer a conexo.
private static nal UUID uuid = UUID.fromString("fa87c0d0-afac-11de-8a39-086626Gc9a66");
I .la a O
O mtodo accept() bloqueante, isto , a prxima linha ser executada somente
depois que algum cliente se conectar. Quando um dispositivo se conecta ao sr
vidor, o objeto BluetoothSocket retornado do metodo accept() e pode Ser UI1 12
para obter a InputStream e a 0utputStream.
.1d
Nota: se voc j trabalhou com sockets em java no ter nenhuma di culdade
para prosseguir a partir daqui. Basicamente vamos criar um servidor de socket
com a classe BluetoothServerSocket, e quando um cliente se conectar e obti o o
socket que a classe BluetoothSocket. De resto, toda a comunicao feita pelas
interfaces InputStream e 0utputStream, como de costume em aplicaes java.
Para encapsular a lgica do chat, vamos criar a classe ChatController, que vai rece
ber o socket BluetoothSocket no construtor, assim como um listener para delegar
os eventos quando uma mensagem for recebida.
ChatControIIer.java
public class ChatController {
private static nal String TAG = "chat";
private BluetoothSocket socket;
private InputStream in;
private OutputStream out;
private ChatListener listener;
private boolean running;
public interface ChatListener {
public void onHessageReceived(String msg);
}
}.start();
}
Agora vamos implementar a activity cliente, a qual vai rC ber um objeto do tipo
BluetoothDevice por parmetro da intent e vai se cone ctar ao dispositivo.
BIuetoothChatCIientActivity.java
public class BluetoothChatClientActivity extends BluetoothCheckActivity
implements ChatController.ChatListener {
protected static nal String TAG = "livroandroid";
// Precisa utilizar o mesmo UUID que o servidor utilizou para abrir o socket servidor
protected static nal UUID uuid = UUID.fromString("fa87c6d0-afac-11de-8a39-9806266c9a66");
protected Bluetoothbevice device;
protected Textview tMsg, tMsgRecebidas;
protected ChatController chat;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_bluetooth_chat);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
tHsg = (Textview) ndViewById(R.id.tMsg);
tHsgRecebidas = (Textview) ndViewById(R.id.tMsgRecebidas);
// Device selecionado na lista
device = getIntent().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
try {
} catch (IOException e) {
error("Erro ao conectar: " + e.getHessage(), e);
}
});
}
@0verride
public void onHessageReceived(nal String msg) {
Log.d(TAG,"onMessageReceived (recebeu uma mensagem): " + msg);
// chamado numa thread, portanto use o run0nUiThread
run0nUiThread(new Runnable() {
@0verride
public void run() {
String s = tMsgRecebidas.getText().toString();
tMsgRecebidas.setText(s + "\n<< " + msg);
}
});
}
@0verride
protected void onDestroy() {
super.onDestroy();
if(chat != null) {
chat.stop();
}
Essa classe recebe um objeto do tipo BluetoothDevice por parmetro da intent e inicia
uma conexo obtendo o BluetoothSocket. Depois de iniciar a conexo, a lgica do
chat delegada para a classe Chattontroller, a qual encapsula como ler e escrever
mensagens na InputStream e OutputStream.
Captulo 36 I Bluetooth .._ -z,.,,,, .- "z ruma
i)s ulT1
No layout desta activity vamos ter apenas um campo de texto Para dlflta
913
r] /res/layout/activity_bIuetooth_chat.m|
<?nl version="1.0" encoding="utf-8"?>
<LinearLayout . . . android:orientation="vertical" >
<LinearLayout android:layout_width="natch_parent" android:layout_height="wrap_content
android:orientation="horizontal" >
EditText android:id="@+id/tMsg"
android:layout_width="0dp" android:layout_weight="1"
android:layout_height="wrap_content"
android:inputType="tet" android:singleLine="true"/>
Button android:id="@+id/btEnviarMsg"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text:"@string/enviar_msg" android:enabled="false"
android:onClick="onClickEnviarMsg" />
TextView android:id="@+id/tsgkecebidas"
android:layout_width="natch_parent" android:layout_height="0dp"
android:layout_weight="1" android:gravity="top"/
Como eu disse antes, a activity cliente vai receber um objeto do tipo BluetoothDevice
por parmetro pela intent. Logo, vamos alterar a classe ListaDevicesActivity de
modo que, ao seleeionarmos um dispositivo da lista, seja feita a navegao at a
activity cliente do chat.
i ListaDevicesActivity.java
public void onItemClick(AdapterView<?> adapterview, View view, int idx, long id) {
// Recupera o device selecionado
BluetoothDevice device = lista.get(id);
// Vai para a tela para enviar a mensagen
Intent intent = new Intent(this, BluetoothChatClientActivity.c1as5);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
startActivity(intent);
l
}
914
Google Android - 4' edio
O cdigo da parte cliente do chat est pronto, falta apenas o servidor. Sendo assim,
crie a classe BluetoothChatServerActivity.
BIuetoothChatServerActivity.java
public class BluetoothChatServerActivity extends BluetooththatClientActivity
implements ChatController.ChatListener {
private static nal UUID uuid = UUID.fromString("fa87c0d9-afac-11de-8a39-08G0266c9a66");
private boolean running;
private BluetoothServerSocket serverSocket;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
I/ Deixa o servidor disponivel para busca
BluetoothUtil.makeVisible(this,39G);
}
@0verride
protected void onResume() {
super.onResume();
// Inicia a thread do chat para no travar a UI
new ChatThread().start();
}
});
// Algum conectou
hCh
chat = new ChatController(socket, Bluetoot a tServerActivity.this);
chat.start();
}
} catch (IOEception e) {
Log.e(TAG, "Erro no servidor: " + e.9etNSS9(), G);
running = false;
}
@0verride
protected void onDestroy() {
super.onDestroy();
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOEception e) {
}
I i
z
r:**. #113
fg..-l-'-.=;' . 'ivz
'~ .z~:,.
.~"'=" ": 'kz .
vsv 1 s 4 z :
* 4 Sen
;1"} ' ' f*"" ',,:?
Sair j
Ficar visivel
Logo depois, pegue o outro celular e faa uma busca pelos dispositivos conforme
a figura 36.5. No meu caso fi encontrado o celular Nexus 5, que o servidor.
917
Captulo 36 n Bluetooth
Nz\vu'= L ix:z=11u.i`~~@<>l5%
QO
I 140
.__ -.
. ^ *Nexu5 i 4r__ s
V W ENVMRMENSAGEM
Enviar mensagem
_.._.___..~....,._.....,_..... V
(i.t\\.
t\\l\i`1*` izi. 'i 'ri I l .t
l
.1``l.lt*v'l:'l\`*
tl
1
Y? C) C] d
I*igtt1t 30.7 - lftti'ittm1 rttettsttgerts tio rltttt.
. . ~ A . mas
Captulo 36 I Bluetooth
Caso esteia curioso de onde tirei esse UUID padrao da serial, isso esta esc
documentao (javadoc) da classe B1uetoothDevce.
Depois de realizar essa conexao, voce pode enviar os dados por Bluet0OIh,
por de baixo dos panos a conexo realizada como se fosse serial.
Essa abordagem est sendo muito utilizada na comunicao de d1spoSltlV0S
Android com Arduino que geralmente recebem os dados pela serial. VOC deve
apenas instalar uma placa receptora de Bluetooth no Arduino para rcalllf 3
conexo, mas isso com voc! Aqui estou dando apenas a dica caso algum d.13
voc precise fazer isso.
http://developenandroid.com/guide/topics/connectivity/bluetooth.html
* cAPruLo 37
A Reconhecimento de voz
.\ 4
`
Reconhecimento de voz est sendo cada vez mais importante nos aplicativos para
dispositivos mveis, principalmente para relgios (Android Wear), culos (Google
Glass) e carros (Android Auto), pois todas essas plataformas esto utilizando cada
vez mais os comandos de voz.
Neste captulo vamos aprender a fazer o Android falar e escutar.
37.1 Introduo
Neste captulo, vamos aprender a fazer o Android falar e escutar e usufruir dos
recursos de voz que so cada vez mais importantes para novas plataformas e
dispositivos como relgios, culos, carros etc.
Text-To-Speech (TTS) a API responsvel por converter texto em voz e fazer o
Android falar, e Speech-To-Text (STT) responsvel por converter voz em texto
e fazer o Android escutar.
O reconhecimento de voz (STT) muito til para interagir com os dispositivos
quando no possvel toc-los, como o caso de enviar comandos para o celular
quando estamos dirigindo.
j a capacidade do Android de falar (TTS) muito til para auxiliar na navega
o do aplicativo quando no podemos prestar ateno na tela. Novamente, o
caso de quando estamos dirigindo, pois o aplicativo do GPS pode falar para nos
orientar sobre as informaes da rota.
Em especial, o TTS muito utilizado para criar aplicativos com acessibilidade
para decientes visuais. Eu recomendo assistir palestra Text-To-Speech 61 Eyes
-Free Project no YouTube, apresentada por dois engenheiros do Google durante
0 Google l/O 2009. O interessante da palestra que um dos palestrantes era
920
- - . _ , ' c)h) ia
Captulo 37 I Reconhecimento de voz
deciente visual e, dentre outras coisas, ele mostrou como essc tipo de ILCH 3
921
Logo aps criar uma instncia da classe TetToSpeech, preciso aguardar a inicia
lizao da engine de voz. Quando o mtodo onInt(status) da interface de listener
for chamado, signica que a engine foi inicializada com sucesso, e assim podemos
usar o mtodo speak(teto,nodo,parans) informando o texto para falar.
tts.speak("Ol Tudo bem?", TextToSpeech.QUEUE_FLUSH, null);
O TTS tambm pode sintetizar a voz e salvar em um arquivo de udio. Isso feito
com o mtodo syntheszeToFle(texto,parans,arquvo).
Por ltimo, quando a engine do TTS no for mais utilizada, voc deve chamar
o mtodo shutdown() para liberar os recursos. Isso geralmente feito no mmd
onDestroy() da activity
// Libera os recursos da engine do TTS
tts _ shutdown( );
922 Google Android - 4' edio
Outro detallie importante que muitas vezes precisamos vericar se o pacote de
dados de voz est instalado no aplicativo. lsso pode ser feito disparando uma intcnt:
// Verica se o pacote de dados do TTS est instalado
Intent checklntent = new Intent();
checklntent.setAction(TetToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResu1t(checklntent, ACTION_CHECK_DATA_CODE);
Depois desta introduo, vamos criar utn exemplo prtico. Crie a activity
Het1oTTSActivity com o seguinte arquivo de layout. Se preferir, abra o projeto Hellovoz
disponvel nos exemplos do livro.
ts /res/layout/actvity_he|Io_tts.xmI
<LinearLayout . . . android:orientation="vertical">
EditTet android:id="@+id/tsg"
android:1ayout_width="natch_parent" android:1ayout_height="wrap_content"
android:tet="01, bom dia." />
Button android:onC1ick="onC1ickFa1arTeto"
ll
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content
android:text="Fa1ar o Texto" />
Button android:onCIick="onC1ickSa1var"
N
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content
android:tet="Sa1var Arquivo" /
Button android:onCIick="onC1ickFa1arArquivo"
II
android:1ayout_width="wrap_content" android:layout_height="wrap_content
android:tet="Fa1ar do Arquivo" />
No layout temos um campo para digitar utn texto. O boto FaIaroTexto vai utilizar
a engine do TTS para falar. O boto Salvar Arquivo vai utilizar a engine da mesma
forma, mas em vez de falar, vai salvar a voz em utn arquivo de udio no SD card.
E o boto Falardo Arquivo vai tocar o arquivo de udio salvo com a classe MediaP1ayer.
O cdigo-fonte da activity pode ser visualizado a seguir:
923
Captulo 37 1 Reconhecimento de voz
j HeIIoTTSActivity.java
@0verride
public boolean on0ptionsItenSelected(Menulten item) {
int id = item.getItemId();
if (id == R.id.action_pt_br) {
Locale locale = new Locale("pt","BR");
tts.setLanguage(locale);
return true;
} else if(id == R.id.action_en_us) {
Locale locale = Locale.ENGLISH;
tts.setLanguage(locale);
return true;
}else if (id == R.id.action_check_data) {
// Verica se o pacote de dados do TTS est instalado
Intent checklntent = new Intent();
checklntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checklntent, ACTION_CHECK_DATA_CODE);
return true;
} else if(id == R.id.action_install_data) {
// Instala o pacote de dados
Intent installlntent = new Intent();
installlntent.setAction(TetToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installlntent);
uma amu: z 4- mm
mmmtmm
l
return super.onotlonsItehSelected(lten)3
}
vetrlde
publlt void nmttttnt mtos) (
Log\d(TAG,En9tne TTS tntctaltzada con xucessoz + Locale.qetDefault());
l Delxa ingles por padro
tts\setLangoage(Locale.getDefault());
}
@0verrtde
protected votd onActtvttyResult(tnt requesttode, tnt resulttode, Intent data) (
super.onActtvltyResult(requestCode, resolttodv data);
lf (requesttode == ACTION_CHECK_DATA_CODE) {
lf (resulttode == TetToSpeech\Engine.CHECK_VOI(E_DATA_PASS) {
toast(Pacote de dados de voz 0Kl)
l else {
925
Captulo 37 u Reconhecimento de voz
@0verride
protected void onDestroy() [
super.onDestroy();
// Libera os recursos da engine do TTS
tts.shutdown();
1
/res/layout/menu_heIIo_tts.xmI
<nenu . _ .
<item android:id="@+id/action_pt_br"
android:title="@string/action_pt_br" app:showAsAction="never" />
<item android:id="@+id/action_en_us"
android:title="@string/action_en_us" app:showAsAction="never" />
<item android:id:"@+id/action_check_data"
android:title:"@string/action_check_data" app:showAsAction="never" />
<item android:id="@+id/action_install_data"
android:title="@string/action_install_data" app:showAsAction="never" />
AndroidManifest.xmI
<nanifest . . .
uses-pernission android:nane="android.pernission.HRITE_EXTERNAL_STORAGE" /
<uses-permission android:nane="android.permission.RECORD_AUDIO"/>
<application . . . />
A gura 311 mostra a aplicao executando no emulador. Veja que no menu temos
algumas aes, como trocar o idioma da engine de voz, assim como vericar e
instalar o pacote de dados de voz.
926 Google Android - 4' edio
. ='` if i
iOla,
_ Voz e __ US
tfrzv 5; ;
*QE 1 swijt . z
Argumento Descrio
texto Idem ao anterior.
int queueMode Idem ao anterior.
Hashiiap params Idem ao anterior, mas agora os parmetros so passados Por um
Bundle.
String utteranceld Identificador nico desta chamada.
Outro mtodo que tambm utilizamos no cdigo o synthesizeToFiie(texto,parans,
arquivo), que converte o texto para voz e salva o udio em um arquivo.
Argumento Descrio #__W ~____M____~___________
ii' A *+,;iia,>;.;arii.a"ist q
Hashap parans Parmetros opcionais como no mtodo speak( . . . ).
String 1eNarfie Caminho do arquivo para salvar o udio.
Esse mtodo tambm foi descontinuado (deprecated) na API Level 21, sendo
substitudo pelo mtodo synthesizeToFi1e(teto,parans,arquivo).
Argumento Descrio _ __
iiii i_"i`r<15}zz{QzQM"" M P'
Hashap pararfis Parmetros opcionais como no mtodo speak( . . . ).
File le Arquivo para salvar o udio.
String utteranceld Identicador nico desta chamada.
Neste tpico aprendemos a fazer o Android falar. O assunto ainda novo e pouco
explorado. Com certeza, se voc for desenvolver alguma aplicao real de voz, ser
necessrio se aprofundar nos estudos, pois o que zemos aqui foi um simples
hello world para te mostrar o bsico da API.
TetToSpeech tts = . . .;
tts.setLanguage(new Loca1e("pt","BR"));
int LANG_AVAILABLE = 0
int LANG_COUNTRY_AVAILABLE = 1
int LANG_COUNTRY_VAR_AVAILABLE = 2
int LANG_MISSING_DATA = -1
Indica que o idioma e suportado, mas falta fazer o download dos pacotes
de voz. Caso seja necessrio, podemos disparar uma intent para fazer esse
download. Mas, geralmente, esse retorno acontece somente para os idiomas
suprtados de forma nativa pelo Android.
int LANG_NOT_SUPPORTED = -2
/res/layout/activity_heIIo_recognizer_intent.mI
<?xnl version=1.6" encoding="utf-8"?
<LinearLayout ... android:orientation="vertical" >
<Button android:id="@+id/btSpeak"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:tet="Clique aqui e fale algo" /
<ListView android:id="@+id/list" android:layout_weight="1"
android:layout_width="natch_parent" android:layout_height="6dp" /
</LinearLayout
Ao clicar no boto, ser disparada uma intent para chamar o aplicativo nativo de
reconhecimento de voz. O resultado ser mostrado no Listview. Note que, como
o reconhecimento de voz no suportado em todos os aparelhos, o cdigo est
vericando se ele suportado antes de disparar a intent.
HeIIoRecognzerIntentActivity.java
startActivityForResuit(intent, 0);
}
};
}
@0verride
protected void onActivityResult(int requestCode, int resultode, Intent data) {
if (resu1tCode == RESULT_0K) {
// Recupera as possiveis palavras que foram pronunciadas
ArrayList<String words = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
1istView.setAdapter(new ArrayAdapter(this,
android.R.iayout.simpie_iist_item_1, words));
}
Ulet pe mui
BaseRecognitionlistenenjava
P8
}
});
HelIoSpeechRecognizerAttivity.java
});
// Inicia o listener do reconhecimento de voz
Intent intent = getRecognizerIntent();
stt.startListening(intent);
}
@0verride
protected void onDestroy() {
super.onDestroy();
// Libera os recursos e naliza o STT
stt.stopListening();
stt.destroy();
}
/res/layout/activity_heIIo_speech_recognizer.mI
?m1 verson="1.0" encodng="utf-8"?>
<LnearLayout ... android:orientation="vertica1"
<LstView androd:id="@+d/list"
android:1ayout_width="natch_parent" androd:1ayout_hetg
' ht="match_parent " />
Apenas para constar, para esse cdigo funcionar, necessri o adicionar a per
misso androd.permsson.RECORD_AUDIO no arquivo de manifesto, mas j zemOS SS0
anteriormente ao utilizar o syntheszeToFi1e(teto,params,arquvo), que C0nVfI 0
texto para voz e salva o udio em arquivo.
A diferena deste exemplo para o anterior que utilizava intents era que no pri
meiro zemos um boto que chamava a intent, ou seja, o aplicativo nativo de
reconhecimento de voz. Desta vez, a activity inicia a engine de reconhecimento de
voz em segundo plano e ca monitorando tudo o que falado. Portanto, se voc
falar algo, os resultados sero entregues no mtodo onResu1ts(bund1e) e mostrados
no Lstview. Faa o teste.
38.1 Introduo
Ant (ant.apache.org) e Maven (maven.apache.org) so dois sistemas de builds bastante
populares, e o Cradle (gradleorg) a evoluo que combina o melhor de cada um.
Tanto o Ant quanto o Maven ou Cradle so assuntos que podem ser abordados
em um livro inteiro, mas neste captulo vamos estudar o bsico sobre o Cradle e
principalmente sobre como ele pode ajudar no desenvolvimento para Android.
A esta altura do livro, vocj deve ter entendido que um dos principais benefcios
do Cradle gerenciar as dependncias, pois por diversas vezes durante o livro
atualizamos o arquivo app/build.gradle para declarar as bibliotecas necessrias.
Para relembrar, as dependncias so conguradas dentro da estrutura dependences,
conforme demonstrado a seguir:
app/buiId.gradIe
apply plugin: 'com.androd.applcaton'
dependencies {
comple leTree(dr: 'libs', include: ['*.jar'])
936
937
Captulo 38 n Gradle
compile 'com.androd.supportzappcompat-v7:22.1.G'
conpile 'br.con.Ivroandrod:androd-uti1s:1.6.6'
}
Como eu disse antes, o repositrio do ]Center padro e ele ser utilizado para
buscar as dependncias mesmo que no esteja declarado no buildgradle.
Mas com certeza o que voc ou sua empresa vai precisar no dia a dia criar bi
bliotecas internas com classes utilitrias.Ao criar a biblioteca, voc ter de decidir
se ela ser livre e de cdigo aberto, como a biblioteca androd-utls que z para
o livro, ou se ela ser privada, ou seja, somente voc e o pessoal da sua empresa
podero utiliz-la.
Bibliotecas livres podem ser publicadas no repositrio global do Maven Central
ou ]Center, e foi exatamente o que z com a biblioteca androd-utls.
No caso da minha biblioteca, eu a publiquei no Maven Central.
http://repol.maven.org/maven2/br/com/livroandroid/android-utils/
E como o]Center engloba o Maven Central, tambm podemos encontrar a lib aqui'
http://jcenter bintray.com/br/com/livroandroid/android-utils/
938 Google Android - 4' edio
Mas o que lazer caso a biblioteca precise ser privada da sua empresa? Nesse caso,
elas podem ser publicadas em um repositrio local do seu computador ou em
algum servidor remoto interno da empresa.
Apenas para car claro o significado de cada tipo de repositrio, veja este exemplo
de arquivo buildgradle. Nele est declarado o repositrio global do ]Center (j
incluso por padro), um repositrio local do computador e um repositrio remoto.
No caso do repositrio remoto preciso instalar o servidor web Sonatype Nexus,
que vamos estudar mais para frente.
app/buiId.gradle
apply plugin: 'com.android.application'
dependencies { . . . }
repositories {
jcenter()
maven { url "lez///c:/gradle/rep"}
naven { url "httpz//localhost:8081/neus/content/repositories/releases/"}
i
rwma l
499
ip'
.zw
Ui
myib t .n y
-HJ |lz
Lt. ~
,Lt_em
,_i_l ,r=:tnzIC'V=.
.. .,, ,s
\ ll"I=7'\'.
- iv fz, '1~
l^.,,fm;i .~.Jl| .mz
ul-.fl
' IM *i,;;' 151 V'
bu kl .HM r
* lu 'n~l'`
5~ :af .'1:
l` \,.2.j`.' 't lt
JY JH? Ui :,*ri."
app/buiId.gradIe
apply plugin: 'con. android . application'
dependencies {
Compile leTree(include: ['*.jar'], dir: 'libs')
compile 'con.android.support:appcompat-v7:22.1.G'
conpile project( ' :nylibrary ')
}
assim que dependncias para mdulos so adicionadas. Feito isso, sempre qug
compilar o mdulo app, todas as classes do mdulo MyLibrary sero adwirmadas no
build, ou seja, podemos utilizar as classes do mdulo MyLibrary no mdulo app. Para
comprovar a teoria, altere o cdigo-fonte da HainActivity do mdulo app para utili
zar a classe Classetilitaria do mdulo MyLibrary, conforme demonstrado a seguir
94 Google Android - 4 edio
MainAttvity.java
inport br.con.livroandroid.nylibrary.ClasseUtilitaria;
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String s = Classetilitaria.hello();
Log.d("livro"," " + s);
}
Por ltimo, vale ressaltar que cada mdulo tem seu prprio arquivo build.gradle;
mas tenha ateno, pois o arquivo mylibrary/build.gradle diferente do arquivo
app/build.gradle. A diferena est na primeira linha do arquivo, pois declarado o
plugin com.android.library para indicar que este mdulo uma biblioteca.
mylibrary/buiId.gradIe
apply plugin: 'con.android.library'
// O restante igual
Isso possvel graas ao conceito de plugins do Cradle. Note que o mdulo app
sempre declarado com o plugin com.android.application, indicando que esse mdulo
executvel, ou seja, possvel clicar no boto Run do Android Studio.
app/buId.gradIe
apply plugin: 'con.android.application'
// O restante igual
Esse conceito importante, pois somente bibliotecas podem ser compiladas e
disponibilizadas para outros projetos. Neste caso estamos compilando um m
dulo que est na mesma estrutura de diretrios do projeto. Masem casos mais
avanados a biblioteca pode ser compilada de forma independente e instalada
em algum repositrio, seja uma pasta local ou um servidor remoto.
. . _ . _ _ g , - f -' (3
38.4 Trabalhando com bibliotecas
Caso voce faa muitos projetos mobile, com certeza sentira a necessidade d
criar uma biblioteca para reutilizar suas classes em todos os seus aplicam/OS. Um
exemplo de biblioteca a androd-utils que estudamos no livro; veja como 618
facilitou os nossos estudos.
Caso tenha interesse em aprender a criar bibliotecas, como a androd-utls que fiz
para o livro, sugiro ler esta srie de trs artigos que publiquei no meu site.
Criando Iibs no Gradle com Android - Parte 1
http://ricardolechetu.com.br/?p=371
Criando libs no Gradle com Android - Parte 2
http://ricardolecheta.com.br/?p=450
A primeira parte do artigo mostra como criar um projeto biblioteca chamado MyLib
e como fazer o build em uma pasta local do computador. Uma vez que o build
feito, o artigo mostra como declarar a dependncia deste repositrio local para
compilar o projeto e utilizar a biblioteca. Farei um resumo da primeira parte do
artigo no prximo tpico, mas as partes 2 e 3 estaro somente no site.
A segunda parte do artigo mostra como instalar e congurar o servidor web
Sonatype Nexus, que um servidor de dependncias corno o Maven. Veremos
como fazer o build e enviar (upload) da biblioteca para o servidor.
A terceira parte deste artigo ensina como enviar uma biblioteca para o Maven
Central, caso voc queira distribuir sua biblioteca de forma global. Foi exatamente
o que z com a biblioteca androd-utls que usamos durante os estudos deste livro.
ToastUtiI.java
public class ToastUtil {
public static void toast(Contet context, String msg) {
Toast.nakeTet(contet, nsg, Toast.LENGTH_SHORT).show()
}
app/bui|d.grad|e
apply plugin: 'com.android.library'
apply plugin: 'naven'
android {
conpileSdkVersion 22
buildToolsVersion "22.0.1"
defaulttong {
// ATENO! FOI REMOVIDO applicationld
ninSdkVersion 9
targetSdkVersion 22
versionCode 1
versionNane "1.0"
}
uploadhrchives {
repositories {
mavenDeployer {
repository(url: "le:///hone/ricardo/gradle/rep")
pon.groupId = "br.con.livroandroid"
pon.artifactId = "nylib"
pon.version = "6.0.1"
}
}
task install(depends0n: uploadArchives)
dependencias {
compile leTree(dir: 'libs', include: ['*.jar'])
compile 'con.android.support:appcompat-v7:22.1.0'
}
_ . _ - ~ item
943
Captulo 38 1 Gradle
Mas espere! Antes vamos aprender a fazer o build da biblioteca. Abra a janela
Gradle que fica no lado direito do Android Studio, ou se preferir utilize o menu
\ew>TooIWindows> Gradle. Essa janela, conforme a gura 38.2, mostra todas as tasks
(tarefas) que voc pode executar. Procure pela task install e d um duplo clique.
Szade rain ' ' * ,..
f
JI "
lr L 5; C *8
"z .=
QI
3
`U
11
., .1Q
;.!t;b.s JJ'
~.,..~ generatee-.easemzetr
geneatF`eleaseEuld~.enfia fi
ai genertePeeaseRes'alues Q,
z. gerifateRelee:eRe;ufces fiO
. , `
' ' I l srAgenev.ateRelea.ecorces
' 7 ' ` ,.:= ;ff,:.z" U
mlalmeuuqndreidlest
M r WY~*P.f'?'*'1 .
zz_
". ..... . ,_ z
:app:k:u.ndleRe3_:se LT?-T3~-ZATE
~ ~ -^ ~~-
1p::upl:e.z::;':es"c`
. -' . l-r'rar*
-C fi*I3C8l-1,
z*~'
;='.- =.-....-._z .-....,,-Y-~'-r
-~*~f~:1'~~1
at _1.ze: z e :.:ar:i.qr::-e :ep
,. ' z
~.....
',* ....
.' .-Z3IEI`IL.J
' `A__~ z-_..
*T~f
`, .pp2l3&-..
*"~ , . ,
i-...-r3E.a Burt
EUIL SUIESSE uL
9. \.
/c;
|"`
nf' f
,
turalmente voc pode alterar o caminho do repositrio conforme preferir.
4i ` i
v R:\nom=\ri(ardo\gr'adle\rep\br\com\|iwoandi o1d\myIib\3.(> 1 - _
~=~ - J J :ep > br torr hr ii U v myhli > 00.1 X
\J
_, ,z> _
h 'l
. rf R J
Qd. Rzzj
Nota: para testar o build, altere a pasta do repositrio para alguma que exista
em seu computador.
Pronto! Agora que j temos a biblioteca vamos criar um projeto para test-la. Crie
um projeto chamado HeI|oLib e adicione a dependncia da biblioteca Mylib. Como a
biblioteca est em um repositrio customizado, ou seja, no est no ]Center ou
Maven Central, preciso configurar o repositrio dentro da estrutura repostories.
app/bui|d.gradIe
apply plugin: 'con.androd.applcaton'
androd { . . . }
repositores {
naven { url "lez///hone/ricardo/gradle/rep" }
945
(apltulo 38 u Gradle
dependencies {
compile leTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.0'
conpile 'br.con.livroandroid:nylib:6.8.1'
}
Uma vez que a biblioteca est declarada. podemos utilizar a classe ToastUtil no
[r)j:tt:
MainActivity.java
inport br.con.livroandroid.nylib.ToastUtil;
public class HainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
ToastUtil.toast(this,"Teste Lib!");
l
}
[ app/buiId.gradIe
apply plugin: 'com.androd.app1caton'
repostories {
maven { url "httpz//servidor_da_sua_empresa:8081/neus/content/repostores/re1eases/"}
}
38.8 Flavors
render a
Depois de estudar o conceito de dependncias e bibliotecas, vamos ap
utilizar flavors, recurso que permite criar diferentes builds do mesmo apl1C1UV0
Existem vrias razes para fazer dois ou mais builds do mesmo apliCaUV0, ff Para
exemplicar vou citar alguns exemplos:
Criar uma verso free e uma paga do mesmo aplicativo.
Criar uma verso de homologao e outra de produo do mesmo aplica
tivo. A verso de homologao utiliza os web services de homologaao e
vice-versa.
app/buId.gradIe
apply plugin: 'com.androd.app1icaton'
android {
defau1tCong { . . }
productF1avors {
azul {
applicatonld "br.com.1ivroandrod.carros.azu1"
}
vermelho {
applcatonld "br.com.1ivroandrod.carros.vermelho"
}
repositores { . . .}
dependences {. . . }
Neste caso foram criados dois builds diferentes, ou seja, dois aplicativos diferente s
cujos pacotes so: br.com.1vroandrod.carros.azul e br . comlvroandroid.carros.verme1ho
943 Google Android - 4 edio
Portanto, o usurio poder ter instalado as duas verses do aplicativo ao mesmo
tempo, pois deixei os flavors com pacotes diferentes.
Logo depois de criar um flavor, abra a janela Wew>TooIVndows> BuiIdVariants, conforme
mostra a figura 38.6. Build variant uma combinao de flavors com build type.
Por padro, o Android tem dois build types (tipos de build) que so: debug e release.
O tipo de build debug usado em desenvolvimento e assina a aplicao com o
certificado debug.kesytore localizado na pasta do usurio no sistema operacional.
j o tipo de build release utilizado para publicar o aplicativo na Google Play
e tambm deve ser assinado, porm com outro certicado que voc deve criar.
Ento temos por padro dois build types: debug e release. E agora no projeto dos
carros temos dois flavors: azul e vermelho. Como uma build variant uma combi
nao de flavors com build type, teremos quatro combinaes que so: azulDebug,
azulRelease, vermelhobebug e vermelhoRelease (Figura 38.6). Portanto, ao executar o pro
jeto, podemos deixar selecionada na janela Build Variants qual verso do build deve
executar. Mas antes disso vamos aprender como customizar cada avor.
uild Voriants ' l*
Test Amact.
Module Build Variant
pp Q, a:ulOebug
5. azu!ReIease
c
5
vermelhcebug j
g_errneihqRelease ___ _ '__
/app/src/vermelho/res
/app/src/vermelho/java
/app/src/vermelho/AndroidManiest.xmI
No caso do aplicativo dos carros, vamos criar apenas a pasta /res para sobrescrever
os recursos, portanto crie as seguintes pastas no projeto:
/app/src/main/res (esta j existe)
/app/src/azul/res
/app/src/vermelho/res
Agora vamos comear a customizar o aplicativo. Para comear com algo simples,
vamos trocar o nome do aplicativo para (arros Azul ou Carrosvermelho, dependendo do
tipo do build. Crie os seguintes arquivos (preste ateno no caminho dos arquivos).
/app/src/azuI/res/strings.mI
<?ml version="1.0" encodng="utf-8"?>
/app/src/vermelho/res/strings.xmI
?xml verson="1.0" encodng="utf-8"?>
A seguir, vamos customizar as cores do avor vermelho. Note que no azul nao vamos
mexer, pois o padro do aplicativo dos carros j azul.
/app/src/vermelho/res/coIors.xmI
Pronto, feito isso, as cores do tema Material para o avor vermelho ja estao fgdas
modicadas. Por ltimo, como o aplicativo dos carros tem um provedor d
950 Google Android - 4' edio
conteudo, precisamos customiz-lo no avor vermelho, pois no possvel ter dois
provedores de contedo com a mesma authority. No arquivo de manifesto do
projeto dos carros, existe a seguinte congurao:
AndroidManifest.xmI
<provider android:name=".domain.CarroContentProvider"
android:authorities="@string/provider" android:eported="true"
android:enabled="true"/>
/app/src/vermelho/res/strings_tong.xmI
<?xml version="1.0" encoding="utf-8"?>
<string name="provider">br.com.livroandroid.carrosvernelho</string
/app/srt/main/res/vaIues/strings_tong.xmI
<?ml version="1.0" encoding="utf-8"?>
string name="provider">br.com.livroandroid.carros</string
<string name="API_KEY">AIzaSyDYCu8-Ijlg-dug-VzT1SC_6fekDKn0oTQ
v sss:Af.a@<i - f`
app/build/.../BuildCong.java
public nal class Buildtong {
// Indica se a aplicao foi compilada como Debug ou Release
public static nal boolean DEBUG = Boolean.parseBoolean("true");
// Pacote da aplicao
public static nal String APPLICATION_ID = "br.com.livroandroid.carros.vermelho";
// Indica o tipo de build
public static nal String BUILD_TYPE = "debug";
// Indica o avor (azul ou vermelho)
public static nal String FLAVOR = "vermelho";
// Version code do build
public static nal int VERSION_CODE = 1;
// Version name do build
public static nal String VERSION_NAME = "1.0";
}
952 Google Android - 4 edio
Essas informaes so muito teis, pois podemos descobrir se a aplicao foi
assinada com um certicado de debug ou release. Caso a aplicao seja assinada
com um certicado de debug, sabemos que se trata do ambiente de desenvolvi
mento e podemos adicionar vrios logs no aplicativo para auxiliar na depurao.
j em modo release, recomendado que todos os logs sejam desabilitados antes
de publicar no Google Play Isso preciso porque, caso o dispositivo seja plugado
na USB, todos os logs aparecero no LogCat. Portanto, tome cuidado para no
expor informaes demais do seu aplicativo em modo release.
Mas o que mais gosto da classe Buildtong a constante Buildtong . FLAVOR, que retorna
o flavor com o qual o aplicativo foi compilado. Usando essa constante, possvel
testar se o build do aplicativo foi feito com a verso azul ou vermelha.
CarroServite.java
public class CarroService {
Dica: no aplicativo dos carros usamos o conceito de flavors para criar duas
verses: azul e vermelho. Lembre-se de que esse recurso pode ser usado para
vrias situaes diferentes, como criar verses gratuitas ou pagas do mesmo
aplicativo, verses de homologao ou produo etc.
Captulo 38 I Gradle 953
38.10 Assinando o aplicativo para o build release
Para publicar o aplicativo no Google Play necess rio assina-lo com um certi
cado de release, que diferente do certicado de debug, o qual j explicamos
por diversas vezes no livro.
O primeiro passo criar o certicado de release. Isso pode ser feito abrindo um
prompt e digitando a seguinte linha de comandofcom rramenta
ae keytool dis
ponvel no JDK:
keytool -genkey -v -keystore carros.keystore -alias 1vro_androd -keyalg RSA -valdty 10000
Voc ter de responder algumas perguntas ao digitar essa linha de comando. Mas
eu particularmente prero criar o certicado utilizando o prprio Android Studio
pelo menu Build >GenerateSigned APK. No wizard, clique em (reate New para criar um novo
certicado. Na janela New Key Store, digite o nome do arquivo, senha do certicado,
o alias, senha do alias e o restante das informaes, conforme a gura 38.8.
Ao clicar em OK, o certicado (keystore) ser gerado na pasta informada. Logo depois
de gerar o certicado, o wizard fornece a opo para gerar um apk assinado com
ele. Mas agora cancele o wizard, pois nosso objetivo era apenas gerar o certicado.
Logiirm. j nn
Qonfrrrnx .N
fmdreid
If--,,.
z fr: .j j j.
foumrv Cade s L
<~~=
5-ia '
~
* 7 z%.. ~ .-51
u PP
build
` src
.gitrgncre
l app.mI
` buildgradle
carroslteystort
app/buiId.grad|e
apply plugin: 'com.android.applcaton'
androd {
sgningtongs {
release {
storeFile le("carros.keystore")
storePassword "carros"
keyAlias "carros"
keyPassword "carros"
}
}
productFlavors {
azul {
applcatonld "br.com.livroandrod.carros.azul"
sgningtong sgningCongs.release
}
vermelho {
applcatonld "br.com.lvroandrod.carros.vermelho"
signngtong sgningCongs.release
}
buldTypes { . . .}
955
Captulo 38 I Gradle
repositories { . . .}
dependenctes { . . . }
E como fazer o build para release? Isso simples, basta selecionar uma das build
variants azulRelease ou vermelhoRelease e executar o projeto.
O build nal, ou seja, o arquivo .apk, ca na pasta app/build/outputs do projeto.
No caso do aplicativo dos carros, ao fazer o build com o certicado de debug, os
arquivos se chamaro app-azul-debugaplc e app-vermelho-debugapk. Ao fazer 0
build com o certicado de release, os arquivos se chamaro app-azul-release.ap/e e
app-vermelho-release.apk.
Outra opo para exportar os arquivos .aple de release utilizar o wizard Build
> Generate Siqned APK. Basta selecionar o certicado keystore de release e preencher o
formulrio. Acredito que isso simples, portanto faa o teste voc mesmo.
htlps://gradle. org/docs/currcnt/userguide/dependcncy_managemcnt.
html #sfc:rcpsirri's
https://dcv'l'pcnandroid.com/sdk/instaNing/studio-build.html
https://devclopcnandroid.com/tools/building/building-studio.html
Android Tools - Gradle Plugin User Guide
http://toolsandroid.com/tech-docs/new-build-system/user-guide
Criando Iibs no Gradle com Android Parte 1
http://ricardolechcta.c0m.br/?p 371
http://ricardolccheta.com.br/Pp 42.3
http://ricardolecheta.com.br/Pp 450
cAPruLo 39
Android Wear
4
39.1 Introduo
O Android Wear permite que voc que conectado ao seu smartphone e receba
noticaes em tempo real. Tem uma interface baseada em cards (cartes), de
forma similar ao Google Now A interao com o usurio feita via gestos e
comandos de voz.
A figura 39.1 mostra alguns dos relgios com Android Wear disponveis no mercado
na epoca que este livro estava sendo escrito. Em ordem da esquerda para direita
esto: Samsung Gear Live, Moto 360 e LG G Watch.
957
953 Google Android - 4 edio
aplicativos para tablets nos quais podemos usufruir de um generoso espao dispo
nivel na tela. A evoluo da plataforma do Android no para, e com o surgimento
dos relgios o foco ter uma interface enxuta, simples e objetiva, especca para
telas pequenas.
Criar interfaces para relgios totalmente diferente de criar interfaces para
smartphones e tablets, portanto precisamos primeiro entender como o relgio
funciona e como deve ser a experincia do usurio neste tipo de dispositivo.
A lista a seguir mostra alguns dos conceitos sobre o design de interfaces para o wear:
lnitiaraplicativos ou cards automaticamente - Usurios esto acostumados a iniciar
os aplicativos manualmente, mas no wear muitas vezes os cards (cartes)
simplesmente aparecem na tela, mostrando informaes relevantes naquele
momento: como um lembrete de uma reunio, alguma mensagem ou email,
uma noticao etc.
Interfaces simples, prticas e rpidas (glanteable) - A interface de usurio deve ser
leve e prtica, pois, quanto menos tempo o usurio levar para entender
o signicado da tela, melhor. O ideal que o usurio bata o olho na tela
e imediatamente consiga entender os dados apresentados. Por exemplo,
cartes que mostram informaes sobre o tempo ou informaes sobre o
horrio de um voo so simples, prticos e concisos, mostram pouca infor
mao, mas o suciente; assim deve ser a interface para os relgios.
Sugesto sobre demanda - Aplicativos para relgio so responsivos e podem
reagir a eventos para auxiliar o usurio em momentos do dia a dia, como
enviar uma mensagem de texto rapidamente para um contato, por meio de
um comando de voz.
Interaes simples - A interao com o relgio toda por meio de gestos e
comandos por voz e deve ser o mais simples e prtica possvel.
Crie o design para telas pequenas - Ao contrrio da tela de um smartphone, o re
lgio deve mostrar poucas informaes e com icones grandes, para facilitar
o toque nos elementos grcos.
Utilize cartes (cards) - Cartes promovem uma interface consistente em toda a
plataforma do Android e representam um aspecto importante do Material
Design. Cartes so simples, prticos e mostram rapidamente a informao
para o usurio.
Respostas rpidas - Facilite a interao do usurio com o aplicativo. Quanto
mais rpido o usurio interagir e executar alguma ao, melhor.
Capitulo 39 I Android Wear
Para mais detalhes sobre os princpios de design para o Android Wear Y@C9mcndO
ler a documentao oficial. Por ora, essa introduo e suficiente, o proximo paSSO
e colocar a mo na massa.
https://dei/elpcrandroid. (`())H/(1('5,{P2/LU('(II'/II(1('X. html
Acredito que a esta altura do livro voc no tenha nenhum problema com relao
a criar um emulador e corn certeza j descobriu que possvel criar emuladores
para smartphones e tablets, Google T\{ Glass Q wear
Ao criar um emulador para o wear, voc deve escolher entre o dispositivo com
uma tela quadrada (square) ou redonda (round). O problema que o espaamento
das telas quadradas e redondas so diferentes e, se aplicarmos o mesmo layout
em ambas as telas, teremos resultados diferentes, conforme mostra a figura 39.2.
A tela quadrada tem 280x280 pixels, e a tela redonda, 320x320 pixels. Embora a
tela redonda seja maior, ela perde muito espao nos cantos devido s margens.
Para solucionar esse problema, foi criada a biblioteca Wearable UI Library, que failim
criar interfaces para wearables. Ento mos obra, faa os seguintes passos:
1. Crie dois emuladores para o wear: um quadrado, outro redondo.
2. Crie um novo projeto no Android Studio com suporte ao Android Wear
(selecione 0 mdulo do wear no wizard).
Por padrao, o wizard do /\ndroid Studio gera dois layouts distintos, um para a
tela quadrada (square) e outro para a tela redonda (round). O layout da activity
tvrnteqvaliitizazi\atw\'watchVewStub,qt1e})erntiu:e(n1ruurar)sla}uts para cacha
tnveh:tela erniiatribt1usapp:rectLayoutt:app:roundLayout.((n1rn1etipcLttela
do dispositivo, o watchvewtub vai escolher o layout correto em tempo de execuo.
/wear/res/Iayout/activity_main_wear.xmI
<androd.support.wearable.vew.watchViewStub
xmlns:android="http://schemas.androd.com/apk/res/android"
m1ns:app="http://schemas.androd.con/apk/res-auto"
androd:id="@+id/watch_vew_stub"
android:1ayout_width="match_parent" androd:1ayout_height="natch_parent
app:rectLayout="@1ayout/rect_actvity_man_wear"
app:roundLayout="@1ayout/round_actvty_man_wear" />
Sendo assim, e possivel ter dois layouts distintos para cada tipo de tela, quadrada
ou redonda. A seguir podemos ver estes arquivos de layout.
zzzo /wear/res/Iayout/rect_attivity_man_wear.mI
android:tet="@
_ - p_content" d ' - _"
androidzlayout width-"wra
t ' N an Fold 1ayUt-h19ht- WFD_content"
s ring/hello_round />
MainWearActivity.java
public class MainNearActivity extends Activity {
private Textview nTextView;
@0verride
});
}
) wear/buiId.gradIe
- ear ,
dependencies {
Compile leTree(df5 'libs', include: ['*-Hf'1>
// wearable UI Library _
conpile 'com.google.android .SU
D port:wearable:1.1.0'
// Google Play Services Pra w . _ rVceS_wearab1e:7.0'0
COMP ile 'com.9009l@-adr1d'9mS'p1ay se
}
962 Google Android - 4 edio
Dica: o HatchViewStub permite utilizar layouts distintos para telas quadradas e redondas,
assim podemos fazer customizaes especcas para cada tipo de tela. Em tempo
de execuo, o layout correto ser aplicado con forme a tela do dispositivo.
Outra forma de criar layouts utilizar um layout nico para ambas as telas,
quadradas e redondas. Isso possvel com a classe BoInsetLayout, que aplica es
paamentos no layout da tela redonda para que o contedo se encaixe melhor
na tela. Para testar, altere o cdigo-fonte do arquivo de layout principal conforme
demonstrado a seguir:
/wear/res/layout/activity_main_wear.xmI
<?xm1 version="1.0" encoding="utf-8"?>
<android.support.wearab1e.view.BoxInsetLayout . . .
android:layout_width="match_parent" android:1ayout_height="match_parent"
android:padding="15dp">
<LinearLayout android:orientation="verticai"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:padding="5dp" app:1ayout_box="a11" >
<TextView android:id="@+id/text" android:tet="@string/he1io_round"
android:1ayout_width="wrap_content" android:iayout_height="wrap_content" />
</android.support.wearable.view.BoInsetLayout>
MainWearActivity.java
public class MainwearActivity extends Activity {
private Textview mTetView;
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_wear);
mTetView = (TetView)ndViewById(R.id.text);
}
) L f z ~. , X HSE
lunldo do layout, assim podemos ver que o Bo I 963
nl llmlt PJU Qllt 0 resultado ~ ' ' ~ . tlayllut Pl um 5P3ainento
seja similar nos dois tipos dg tela
Para nalizar os conceitos bsicos sobre criar interfaces e layouts para wear,
vamos olhar como funciona os temas. No caso do wear, so utilizados os temas
Theme.DevceDefau1t ou Theme.DeviceDefault.Lght, portanto se voc olhar no arquivo
de manifesto encontrar a congurao de tema da aplicao:
<manfest _ . . >
<app1caton . . .
androd:theme="@androd:style/Theme.DevceDefau1t" >
</app1icaton>
</manfest>
' frefa
.\ . \ Y uece
_ C ` . `- .\ l.._
39.3 Conectando o smartphone n0 dmld Wei"
i vemos fazer para desenvo lve
r vara
1 wear e conectar o
.. 3 Anroi
\f` * 3 _ . z
A primeira ta q I, io PW, 550 voc vai pfgusr de um smartphone (dis
Smanphom mm O Kdogd 4d iu suPCrior com o Google l)lly5L`I`\'l&`L`h mst.il.ido.
plmmo
No seu full)
smartpimmm,
f download
Ka do aplicativo do Android Wear dispomxcl nt
Google Play
964 Google Android - 4 edio
Para conectar (parear) o smartphone com 0 emulador do relgio, conecte o
smartphone na USB e digite o seguinte comando no prompt:
adb -d forward tcp:S601 tcp:5601
Feito isso, abra o aplicativo do Android Wear no smartphone e utilize o menu (onettar
Emulador (Connect Emulator) para fazer a conexo com o emulador. A gura 39.5 mostra
o aplicativo do Android Wear conectado no emulador do wear.
A comunicao entre os dispositivos feita com Bluetooth, portanto certifique
-se de que o Bluetooth do smartphone esteja ligado. lsso significa que, para os
dispositivos estarem conectados (pareados), eles precisam estar prximos e no
raio de ao do Bluetooth, que algo em torno de no maximo 8 a IO metros.
Desconr-:tar nwultor
Esquecer Emulador
Card 5 de 1t~mc~rslrano
i.4i.>'| al
. ,,,
i it`lif1I de ixiq .. tw '.~.'.|*.ii7lz -.
az ao c ~ . ,
. r no, menu_Ogl().
Chca
Smartph()n
_ mostradas eno
onectarSero
o smartphone
asno
no, orel'
- , , _ oatiOo de
as
x 'inI ~_ .
ga,-ds C e(D-.P0rao.ad-Recomendo
`wear
Isso ft d~ Lb tb mdf*
l10[lC'1`*`
~
de Demonstrao _, voc
~ 1 1. _
disparar vamos x _ ~ ards)
lc
deno aplicativo doarAndro d Pam
-.`8
exemplos noticaoes 1 eat
Ja tem uma boa Ideia d . . I P Lmulador do wear. Assim voc
-z _ ___-. ..-___.__ P _ terfdce refomcnddi Pra o relogiu
39.4(
onectando o smartphone no relgio fsico
Caso voc tenha um relgio com o Android Wear e queira utiliz lo no lu ar do
emulador siga
, g passos paraos se uintes
conecta '_smartphone:
lo ao seu .
1.
No relgio, entre na tela de (onguraes>Sobre(Settings>About) e clique no item Nmero
do Build (Build number) por sete vezes. Isso vai habilitar as opes de desenvolvedor.
2. Entre no menu das Opes do Desenvolvedor(Deve|oper0ptions) e habilite o debug pela
USB.
ocial: .
~ ' ` d Android Wear voc vera que os is
6.
' ` de
Na tela foram
positivos conectadoos
Conguraoe com as
aphcanvo mensag
O ens '
Host- connected / Target: connected.
Cl. m-nOlCmll
dor
- /d lo er android.com/training/wearables/aPP5/bt'debggmg`html
httpsi/ eve p i tilizar o emulador do Wf Para lcslar Os exem
21
De qu al uer forma, fewmendo U
plos d o livro, pois todos funciona
966 Google Andoid - 4 edio
39.5 Noticaes no wear
\ W" U 'flyli \`l\cl.\d zu |u;\rlplum'. rmlns as uu ic:\:s du snxznrtplmnc
.\p\|'vu'|x1 uu n*I`gm_ I\I;s,c;1s xfnu quz-ir;|, v pussvcl pclu API couigumr uma
"*'''\`4K P;lI`.l .\|\L`I;Ih .IP.\I`\`\`\`l` uu s|u;u'lp|unc uu rc|'gi.
\`.lI`.l l\l`l\L`.\l` uuu .ns 1\liIic;\g`vs_ nlunn pmjctu HeIION0tifI(ati0n quc izcnuws uu
\~_w1tulu Ji sulur 1utiix1g(cs. v lispnrc .us l\(\liL`;IL`S no SlT\1ll'I[)h()l\L` (cstnndu
\`Ul\L`\`l;\\lU |uvuu1l.ulr| \\'m1`)..\u mm'is_ulns;1mxifiz1`L-5 scrn|11>;
lI.l\l.l uus|u1rlplu|u*v |ucuu||.u|m'du\\'c;u'.
.~\ luzum Wi umstm ;\ m1iliu.1g.`n uuplcs lu p|'cu HeIIoNoti(ation no cnuxlulm' du
\\\~.u'. NoLulucsq1u'ulul;1 i3u1|'.1\.1 |\liic;n.`|n;1p;11'u'cuu Ich iuirinl c.msclcci|\;i
1.1 v.1r1.`n \c;uxl\ sulwc pnm x`IllI;I qxuwc D. Nu lL`l`\`L`I'.l |mrlc da figura. plc|ms
W131 p.\;zu\.1 quc umsuxu ;\ npginw p;ux1 .ulwrir .1 |m1iiu1;1 uu \lll;ll`I[1h(\l1L`. Scmprc
quc uum ililvlll L`Sl\'L`I` ;1ssm.ul;1 C1 mui lic;1. l p;`1 Open on phone vstzuxi dispm\'cI.
Mmwdomuma
o NoticationWearUtiI.java
Em qualquer lugar do codigo (do smartphone), chame este metodo para criar a
ncnicaco:
NotcationwearUt1_createPagesNotcation(this);
ll
No smartphone somente a primeira noti cao ser exibida. porm no wear voc
ver as duas paginas conforme a gura 39.9. Se quiser conlerir o exemplo funcio
nando, abra o projeto He|IoWearNotications no Android Studio. Lembre-se de executar
o mdulo mobile no smartphone e 0 mdulo wear no emulador do Android \\/ear.
` _.
Segunda
Pnnqea llellsflgelll
mensagem
Mensagem 3 Mensagem
i Mensagem 3
Mensanern? Mensa
9 er1
Figura 3910- Noticaes empilhadas.
.elc:c::o;;;t;:(nUtl
i u que cria uma da notlcaao
biblioteca android-utils
agrupada. contm um mtodo
O cdigo 0
mesmo utilizado para criar qualquer outra noticao, com exceo do mtodo
setGroup(groupId), que recebe o cdigo do grupo. Notificaes que forem criadas
com o mesmo cdigo de grupo sero agrupadas automaticamente no wear.
1:
eg NoticationUtiI.java
public class Noticationutil {
l,t~
Em
}
qua
// Cria a notication
NoticationCompat.Build er builder = new NoticationConpat.Builder(contet)
QUCT
1 1 d digo
.setContentIntent(p).setContentTitle(contentTitle)
.setContentText(contentTet).setSnallIcon(smallIcon)
.setGroup(groupId)
.setAutoCancel(true);
UE-Ear chame
(do smartphone),
not1hca,ao.VC]1 aus C5 _ O*)WOG)Qw
. V `~ ~, 'tou passando sempre L
C este
'd' metodo
do *ru ocrllf fi
para
E P
1,"zwpx",1nrenr,R.fvav-1a'*'~
111, 1I|);
NotificationUtil . createStackNotificati0 ( UWS ,
97 Google Android - 4 edio
NoticationUtil.createStackNotication(this,Z,"GrupoXXX",intent,R.mipmap.ic_launcher,
"Titulo 2","Mensagem 2");
NoticationUtil.createStackNotication(this,3,"GrupoXXX",intent,R.mipmap.ic_launcher,
"Titulo 3","Mensagem 3");
NoticationWearUti|.java
public class NoticationwearUtil {
public static void createRemoteInputNotication(Context context, Intent replylntent) {
// Intent para executar no smartphone ao responder
Pendinglntent replyPendingIntent =
Pendinglntent.getActivity(context, G, replylntent,
Pendnglntent . F LAG_UPDATE_CURRENT) ;
// Remote Input
String[] replyChoices = contet.getResources().getStringArray(R.array.reply_choices);
Remotelnput remotelnput = new Remotelnput.Builder("remote.input.key")
.setLabel("Resposta") // Titulo
.setChoices(replyChoices) // Array com respostas
.build();
NoticationCompat.Action action =
new NoticationCompat.Action.Builder(R.drawable.ic_action_reply,
"Responder", replyPendingIntent)
.addRemoteInput(remotelnput)
.build();
// Cria a noticao
Notication notication =
new NoticationCompat.Builder(contet)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Lembrete")
I
Captulo 39 z Am|d we f
97
.setContentTet("Voc vai feSta,") 1
Not`F ' . .extend(new Notificationton
t. l
.bu1d(); Da HearabIeExtender().addAction(action))
I'catl"Ma"a9fCWDt not1cationManager z
Not'f
z . Q $_
n t`f . IICt10HdgFCONDt.fF0(C0tXt);
} O 1'Cat1nMa"a9r~1fy(1, notcation);
}
/mobile/res/vaIues/arrays.xmI
<?m1 version="1.0" encodng="utf-8"?>
<5tf"9'FFy @="rep1y_choices">
<'Ef'1>5|'l</tI'1> <.tem>N0</tem> <tem>Ta1vez</tem>
Para testar as noticaes com resposta por comandos de voz, basta chamar um
cdigo como este e denir a activity que vai receber a resposta:
Intent intent = new Intent(ths, Rep1yActvty.c1ass);
Noticationweartil.createRemoteInputNotcaton(this, ntent);
RepIyActivity.java
public class Rep1yActivty extends ActonBarActvty {
@0verrde
B ndle savedInstanceState) {
protected void onCreate( u
supe r.onCreate(savedInstanceState);
setContentView(R-HYOU-activity-reply);
getSupportActionBar() ___
.setDisp1ayHomeAsUpEnab1ed(true);
return null;
}
Caso queira conferir o resultado, este exemplo tambm est disponvel no projeto
HeIIoWearNotications. A gura 39.11 mostra o resultado da noti cao. Ao rolar a tela para
ir
o lado, o usurio pode clicar no boto Responder. Para responder, possvel utilizar o
comando de voz caso voc tenha um relgio real, ou escolher uma das opes que
so previamente apresentadas na lista. Ao escolher a resposta, a activity recebe o
retorno pela intent, mas isso voc ver por si mesmo ao executar o exemplo.
G91@ApiCIient .
. 97
GoogIeAptCIient - new GOOQIQA ' 1 3
.addConnectionCaII.backs(new ConnectionCaIIIi)ifk;f;t Bui1der(this)
Dubli '
.add0nC t' ' - ~ ~ -I I )
pubuc void onConnected(BundIe connectionint) { _ _ _}
c void onConnectionSuspended(int cause) {
pub{::nq):nFqiIedListener(new 0nConnectionFaiIedListener()
i on onnectionFai1ed(Conne t' R {
// Adiciona a Hearable API C Ion esult result) {' ") } II
.addApi(Hearab1e.API)
.buiId();
ar. A
lhe deve serefamili
- ,3
Como j estudamosgleo Play
Goo P no livro acredito que este cdi o
Services
p nas observe que estamos adicionando a Wearable API no
mt(?d0 add^P1(pi). Lembre-se tambm de que a dependncia do Google Plz
SfVlCS deve ser declarada no arquivo buildgmdle do mdulo Uma vez que);
conexao com o Google Play Services foi estabelecida, podemos usar as bibliotecas
II); , ,
public void onResuIt(NodeApi. e
_St<N0de, nodeg z getConnectedNodesResuIt.getNodes();
if (nodes gz null && !nodes.isEnpty()) {
Node ngde : I'\0deS.gt(0);
nodeld : |'\0d.getId(); Este e O nodeld
' e mais - _, - . .
I
listener queomtora
m I _se 0 dispositivo es
basta utilizar este cod1g0.
974 Google Android - 4 edio
Goog1eApiC1ient mGoog1eApiC1ient = .
NodeApi.NodeListener nodeListener = _ . .;
wearable.NodeApi.addListener(nGoog1eApiC1ient, nodeListener);
. ` ` 3" X mostrei ` ` .f ,.
~ 2 IL a entre os dia . . ara trocar
iuhmnaes de fmn r ipa poncagao Y etc.AMessage
~ POSlIlv05, API foi criada p
se
.os. -,di.`_' ~_ ',. _
tura sero automaticamente ncromzados
si ~ ` entre o smartphone Q O \vearable. Mesmo
5P05mV05 estelam d5C0nCI2!Cl0S, a sincronizao ser feita ao conect-los.
l dra enviar e armazenar mtormaoes na area de compartilhamento com a Data
API. podemos utilizar um cdigo como ester
Bundle bundle = new Bund1e();
bund1e.putStrng("nome",Ricardo);
String path = "/nsg";
PutDataHapRequest putDataHapReq = PutDataHapRequest.create(path);
Dataap dataap = Dataap.fronBund1e(bund1e);
putDataHapReq.getDataMap().putA11(dataHap);
PutDataRequest putDataReq = putDataHapReq.asPutDataRequest();
Nearab1e.DataApi.putDataIten(nGoog1eApiClient, putDataReq);
dispositivos.
C.oog1eApiCIient G009l~^0U~"'t = ~ '*
DataApi.DataListener dataListener = . .dataLrstener);
1 ApiC1ient, .; _
Hearab1e.DataApi .addListener(nG009 0
Listener contem apenas o mtodo onatathanged(dataEventBuffer).
A interface DatAD-ta
ataEventBuffer) {
public vo id onatathanged(DataEventBuffer Cl
for (DataEvent event : dataEvent8uffer) {
DataEvent.TYPE_C"^"5) {
if (event .getTyP@() =*
976 Google Android - 4' edio
Datalten tten = event.getDataIten();
if (ten.getUrt().getPath().conpareTo("/nsg") == 9) {
Datarlap dataap = Datarlaplten.fronDataIten(ten).getDataMaD();
String none = datallap.getString("none");
} else if (event.getType() == DataEvent.TYPE_DELETED) { _ . .}
I
I
Esse cdigo veri Fica se o evento gerado do tipo DataEvent.TYPE_CHANGED, para depois
obter o objeto Dataltem que contm os dados. Um Datalten composto do path
(caminho) que identica os dados e um Dataap que funciona da mesma forma
que um Bundle. Vale lembrar que a Data API pode ser usada pelos dois lados,
sendo que ambos, smartphone e wearable, podem armazenar e ler as informaes.
mobile 64 az Q. .-
ri 'l`a\*L
^%^nuwu .
`f Li
` 'att
i ~ ` 11t ,zl'Lvll\1()ld
v
-\, _ i`z->1l_\_Jlp,
WF.
br r r~_z --. _/oc
L z 'zzn ,'z>t>L;i._zr\,. ,
shared
L
`C`|3r1F(55
oicorn li~.'roandroid.shared
||I|||||IiEIME5EEi|||||||||||||||||IIIIIIIII
'~ tzr.com.l,roandrod.shared .~ ~
l ' F
f' " Weaf
lcfi F
z
21 Vcs
"= ^_ _. v
" orzdle Scrlprg
WearUti|.java
package br.com.livroandroid.shared;
ndDeviceNodeId();
// Ao conectar, liga a DataAPI e Message);
- API
public void onConnected(BUd1@ COHHECOHHIH) i
ices ononnected!
tg.1Ao, "Play Serv
munmnnlzzuuuzu
ifdatListenr I nult) {
1ezd1. ddrieenef tA@\1
Hoorcslszitapiza dttscnoftnunglhlittlnt1 liiLiI0F11
1
iftmvaaliter l ull) (
Lqzdcfu. aduiernef n@^9ii
iaf|bteznesgeAptzaddtisteneftnooplohpttiont, n|9o&ttenuf)
1
1f(f1H9|'wv ! 69111 1
Lea d(1A. uifenr Ned@Api1
weofbtezndepizdaistnerneegteApitient, nod0Ltenef)
1
dvvffi
pul1 vx @nraee1i@us@d(int @u@1 L
Lazaia, anenfinupnde " z ui
1
11
zdnn1tnfi\dLit@f(w Ge919i1ienzncanneienri1edLitf( L
v fi
pHh1\ vaia 86G0i1Q(GH@16U1f ruit (
L;G(1Gz "enentienrite " 1 f@u1t)
1
11
zdB(H@fizF)
zE911(1;
1_
bmi wxa f@fLi@@r(@@A1z1Lttf ttttef) {
thizdtrxtf ttiffg
1
now ResultCallback() l
aralz gogeApl.getConnectedNodes(mGoogleAplClet)-S@fR5U1tCl1bk(
@0verrlde
l.GetConnectedNodesResult getConmectedNodesResult) {
public vold onResult(Nod^P
n.`n;
LSt<Nde, ndgs = getConnctedNodesResult-9@fNd5()3
if (nodes yz null da !modes.lsEmpty()) {
Node node = nodes.9et(@)i
odald n mod! 90f1d()5
nQdQId, Q* nodeld)
}
);
l
930 Google Android - 4* edio
// Cria un Asset a partir de un Bitnap
public Asset getAssetFron8itnap(Bitnap bitnap) {
l- 3VteArray0utputStream byteStrean = new ByteArray0utputStrean();
bitnap.conpress(Bitnap.ConpressFornat.PNG, 196, byteStrean);
return Asset . createFromBytes(byteStrean. toByteArray( ) );
}
O cdigo desta classe grande, mas no se assuste, pois boa parte referente
conexo do Google Play Services, e o restante apenas para encapsular as APIs
do Wear. Essa classe encapsula o cdigo necessrio para se conectar no Google
Play Services e quando o mtodo onConnected(bundle) for chamado estamos adicio
nando os listeners da Data API, Message API e Node API caso eles tenham sido
informados pelo cdigo cliente.
Na classe tambm criamos o mtodo putData(ath,bundle), que demonstra como
compartilhar dados com a Data API, e o mtodo sendMessage(path, bytes[]), que
demonstra como enviar uma mensagem com a Message API.
O importante desse cdigo que para enviar uma mensagem para um dispositivo
necessrio descobrir o id do n de destino, ou seja, o id do outro dispositivo, seja
smartphone ou wearable. Para isso, foi criado o mtodo ndDeviceNodeId(), que utiliza a
Node API para buscar o id do dispositivo conectado. O id armazenado no atributo
nodeld da classe para depois ser utilizado para enviar as mensagens pela Message API.
Vamos prosseguir e criar um exemplo para enviar mensagens entre o smartphone e o
wearable. No mdulo mobile altere a HainMobileActivity, conforme demonstrado a seguir:
MainMobiIeActivity.java
public class MainMobileActivity extends BaseActivity {
private Heartil weartil;
private int count;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_man_nobile);
Captulo 39 I Android Wea;
WGFUI : n 981
ew HearUtil(this);
}
@0verrid@
@Override
ac: y _
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ferrari_ff);
Asset asset = wearUtil.getAssetFromBitmap(bitmap);
b.putParcelable("foto", asset);
// Compartilha os dados com o wearable
wearUtil.putData("/msg", b);
H_
}
}
e wearUtil e dene
A tivit conecta-se ao Google Play Services com a ajuda da class
os mtodos onClickSendMessage(view) e onClickPutData(view) para envrar mensagens.
Para chamar esses mtodos, vamos adkjonarchslxnesnolayoutdaacvny:
/mobiIe/res/Iayout/activity_main_mobiIe.mI
' droid:
an . _ ex = _ _ ,, H
<LinearLayout . . .>
t t "@string/hello world"
<Tetb;1:Tlay0ut width="wrap content" android:laY0Ut-he19ht= "rap-Content />
No mdulo wear vamos fazer a outra parte do aplicativo, a qual vai receber as
mensagens. Neste exemplo vamos receber uma mensagem simples pela Message
API e outra que vamos ler os dados compartilhados pela Data API.
No arquivo de layout vamos adicionar um Textview para receber o texto enviado e
um Imagevew que vai receber o Bitmap (Asset) quando os dados forem enviados pela
Data API. Para denir o layout da activity do wear, temos duas opes: a primeira
utilizar o layout HatehVewStub e especicar um layout diferente para cada tipo de
tela (redonda ou quadrada). A segunda utilizar 0 layout BoInsetLayout, 0 qual
permite utilizar o mesmo layout para ambos. Por padro, o wizard gera os arqui
vos seguindo a primeira opo, com os layouts separados para rect e round. Mas
para simplicar o exemplo estou usando a segunda opo, com um layout nico.
/wear/res/layout/artivity_main_wear.xmI
<?n1 version="1.6" encoding="utf-8"?>
<android.support.uearab1e.vieu.BoxInsetLayout
xn1ns:androd="http://schemas.androd.com/apk/res/androd"
xn1ns:app='http://schemas_androd.com/apk/res-auto"
androd:1ayout_wdth="match_parent" android:1ayout_height="match_parent"
androd:paddng="15dp">
<LinearLayout androd:orientation="vertica1"
androd:1ayout_width='match_parent" android:1ayout_heght="match_parent"
android:padding='5dp" app:layout_bo="a11"
TextVeu android:id="@+d/text"
android:layout_wdth="wrap_content" android:1ayout_height="wrap_content" />
<InageVieu androd:id='@+d/img"
android:1ayout_width='wrap_content" android:layout_height="wrap_content" />
</androd.support.wearable.vew.BoInsetLayut>
@0verride
protected void onResume() {
super.onResume();
wearUtil.connect();
}
@0verride
protected void onPause() {
super.onPause();
wearUtil.disconnect();
}
@0verride
public void onDataChanged(DataEventBuffer dataEventBuffer) {
for (DataEvent event : dataEventBuffer) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
// Dataltem changed
Dataltem tem z event.getDataItem();
if (trem.9eturi()-9@tPath().c0npf@T("/S9") == 0) {
// L a mensagem enviad pela Data API i M ()
oatmap dataMaD = DatMrIt@'-fff"afa1t'('")'gewata ap
final string uso = dfa"P-9'5""9(""'s9");
nal int count = dataMaP-9t"t("C""t")
A et asset = dataap.getAsset("f0t0");
lna 1 N '
fssl B.t ap bitmap - weartil.getBitnapFronAsset(BSSG);
run0nUiThread(new Runnable() {
@0verride
public void run() {
nTetView.setText(nsg + "\nCount: " + COUH);
img . setInageBitnap(bitnap);
}
}):
}
i
@0verride
public void onHessageReceived(nal Hessagevent nessageivent) {
Log.d(TAG, "onMessageReceived(): " + messageEvent.getPath());
// L a mensagem
String path = nessageEvent.getPath(); // o "/msg"
String nodeId = messageEvent.getSourceNodeId();
byte[] bytes = messageEvent.getData();
nal int count = bytes[0]; // L os bytes
run0nUiThread(new Runnable() {
@0verride
public void run() { // Atualiza a view
mTextView.setTet("Count: " + count);
}
});
}
Como podemos ver, a classe wearutil facilita a conexo com o Google Play Services
e ainda encapsula a utilizao da Data API, Message API e Node API. Observe
que, quando o aplicativo do wear recebe a mensagem, utilizado o mtodo
run0nUiThread(runnable) para atualizar a interface, pois as mensagens so recebidas
em uma thread diferente da UI Thread.
Dica: ao lado do boto Run do Android Studio, voc pde selecionar qual mclul
ser executado, neste caso o mdulo mobile ou wear. Execute o mdulo mobile em
um smartphone e o mdulo wear no emulador d wear.
.__f..,
l'i~_`ilU wirld'
SEND MESSAGE
PUT DATA
A segunda parte da figura inostta os dados compartilhados pela Data API. Neste
caso foi enviado o mesmo contador (int) por um Bundle, junto com uma string
para mostrar uin texto e inais uma imagein no formato Asset. Um Asset representa
algum dado binrio, como uma iinagem. O que zemos foi obter um Btnap dos
recursos do projeto e converte-lo para o tipo Asset. A vantagein de utilizar assets
e que a Data API faz cache deles com o objetivo de otimizar a conexo Bluetooth
entre os dispositivos.
Nota: no exemplo com a Data API, note que estamos trabalhando coin a classe
Bundle, nossa velha conhecida. Mas internamente o Bundle e convertido para o
objeto Dataap da API do Wear. A classe DataMap tambm funciona na estrutura
de chaves e valores.
3'iaqI.|(l.id.btE:vi.atfotn).setihClidd.i.stener(neu Vieu.(h(lick'Listener() {
Overricb
ptblic void onElid(Vi.ev v) {
I/ Valida se o arquivo existe
if (Ele != null Il le.erlsts()) {
int u = 159; I/ Hmda a foto reduzida em 150x150 pixels
int h = 158;
BUW bit!!! = I!'El;lEsi1eltils. ` `.fruflle(Gle), uu, h, false);
/I Converte o bitnap para asset
Asset asset = uearutil.9etAssetfru6itna9(biu\ap);
il Envia o asset para o wear
! il! = 3 IIEO;
'l.e.Ithrtell.e("fv't|', sset);
IEUW-|ltht('Ifut1', bnile);
I
l
});
l
Captulo 39 I Android Wear 937
protected void onResume() {
super.onResume();
uearUtil.connect();
}
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_main);
ueartil = new HearUti1(this);
wearUti1.setDataListener(this);
img = (lmageview) ndViewById(R.id.img);
}
});
}
A gura 39.15 mostra o resultado. Foi tirada uma foto pelo celular e enviado ao
wear utilizando a Data API.
989
(pll.IlO I Android wear
!
--
"***www
1 /wear/res/layout/activity_card_fragment.xmI
<android.support.wearabie.view.BoInsetLayout . . .>
<FraneLayout android:id="@+id/cardLayout"
android:iayout_width="match_parent" android:iayout_height="match_parent"
app:iayout_bo=bottom" /> `
</android.support.wearabie.view.BoInsetLayout>
CardFragmentActivity.java
Para testar esse exemplo, execute o projeto nos dois dispositivos e depois clique
no item Cardfragment na lista do smartphone. Feito isso, uma mensagem ser enviada
para o wear, resultando na chamada da activity CardFragmentActivity. O resultado
pode ser visto na gura 39.16.
992 Google Android - 4' edio
MDQOID
Meu Primeiro
l8HiVHHN
F@unz3QH-(ludfnuynwn.
(aso voc qucira customizar a vicw do carto, basta criar uma subclasse dc
CardFragnent c sobrcscrcvcr o mtodo onCreateContentVew( . . . ), conforme dcmonstrado
a seguir. Dcssc modo, voc pode rctornar o layout que quiser:
fu (ustom(ardFragment.java
public class CustonCardFragnent extends CardFragnent {
@0verrlde
protected View onCreateContentView(Layoutlnater nater, Vewroup container,
Bundle savedInstanceState) {
view view = inater.lnate(R.layout.fragnent_custon_card, container, false);
Textvew tTitle = (Textvew) vew.ndVlewById(R.d.tTtle);
tTitle.setTet(getArgunents().getStrng("ttle"));
Textvew tHsg = (Textview) view.ndVewById(R.id.tMsg);
tHsg.setTet(getArguments().getStrng("msg"));
return view;
}
Q* /wear/res/layout/fragment_custom_card.mI
LnearLayout android:orentaton="vertcal" . . .>
<TetVew androd:id="@+id/tTtle" androd:tetColor="#0G0000"
androld:layout_wdth="wrap_content" androld:layout_helght="wrap_content" /
<TextView android:id="@+d/tMsg" android:tetColor="#00G000"
androd:layout_wdth="wrap_content" androd:layout_heght="wrap_content" />
</LlnearLayout
(ustomCardFra
gmentActivity.java
super.onCreate(savedInstanceState);
setContentVew(R . layout . actvity_card_fragment);
FragmentManager fragmentManager = getFragmentManager();
FragnentTransacton fragmenransacton =`fragmentlanager.begnTransacton();
CustonCardFragnent card = new CustonCardFragnent();
Bundle args = new Bundle();
args.putString("title","CardFragnent");
args.putString("msg","Card custonzado.");
card.setArgunents(args);
fragmenransacton.add(R.d.cardLayout, card);
fragmenransacton . conmt( );
}
CardFragment ,
Card customizado.
landroi.d.support.vearable.vi.eu.CardFrane
/android . sipport .uearable . view . CardScro1lVieu
/android.support.vearab1e.vi.eu.BoxInsetl.ayout
A Figura 3918 mostra o resultado deste exemplo Este o item Cirdfrime do projeto
de exemplo
Titulo do
Card
Texto do Card aqui
Com ~za'as tinhas
Em minha opinio sempre que possivel a soluo mais simples e utilizar a classe
CardFragnent que j esta pronta. O codigo mais simples e ele cria facilmente 0
visual padro dos cartes alm de ja utilizar uma imagem de icone, dando uma
boa aparncia e acabamento ao carto Crie um carto customizado realmente
Capitulo 39 I Android Wear 995
. _, isso
se voc precisar customizar a view, pois conforme vimos exige` um pouco
mais de codigo.
. lmnulfs
t l*'i=f\*l*" . montes
.3
2mnutes 8 :mm
. o -z
;i:iz:i=,iis . mm
. B mnutcs
O exemplo que vou demonstrar vai exibir a lista de dez carros no relgio. Por ques
tes de usabilidade, isso talvez no seja pratico, mas o objetivo aqui e aprendcrmos
a criar a lista. Para continuar,crie~ a~l
c asse Carro, conforme demonstrado a seguir:
fi (arrojava
public class Carro inplenents Serializable {
public String none;
public int ing;
public Carro(String none, int img) {
this.none = none;
this.ing = img;
}
/wear/res/layout/activity_heIIo_Iistview.xmI
<android.support.wearable.view.BoxInsetLayout . . >
<android.support.wearable.view.HearableListView android:id="@+id/listView"
android:layout_height="match_parent android:layout_width="match_parent" />
HeIIoList\ewActivity.java
@0verride
public void onClick(HearableListView.Viewolder v) {
Integer position = (Integer) v.itemView.getTag();
Carro c = carros.get(position);
}I
Captulo 39 n Android Wear
Toast.nakeTet(th5,"(rro.
= Q I . _ HORl)_ .. + C
h nom
. T 997
Intent ntent ii ` - e, oast.LEN;rH 5
nt Nent.DutEtra( carro ,) , S mf),
W @"(thts,CarroActvty.c1ass)~
startACtVY(ntent)
@0verride
O c ` _
} public void onTopEnptyR@9n1ick() {}
adapter.
.7 O`._
d180 dO adapter nao vou mg strar
O resultado deste exem 10 d .
ue I _ P P0 e ser visto na um 397 _
C1 , ao se ecionar um carro estamos nave
8 paradoutra
E z __0_
no livro '
, pois c como qualquer outro
Inclusive
activity portantoveja
os
conceitos ue voc ` '
wear da mesma
7 Q Ja conhece forma ml
sobre desenvolvimento And 53
'd ~*
_' aplicados no
Q Ferrari FF
AUDIGT
Spyder
Porsche
Panamera
ar e um exemplo classico e ga er
que vamos mostr ' ` " ' * d * I' l* ii de fotos, a qual e visualizada
e sincronizada entre O smartphone e relgio.
A figura 39.19 mostra o exemplo. A mesma lista de dez carros toi criada para
. - - , . ' ' ' Har, ~, a nave tar
preencher o ViewPager, portanto voc pode navegar entre os carros lateralmente.
O VtewPager foi criado tantozno -mafaplicativo
.,tainbem
. ' z 1;sera
)\\'L`11I`,C\'iLL`-\'L`l'S;l.
mobile quanto
atualizac int no ue c tg zu
numa pgina do mobile, esta pag
C21
Isso e possvel utilizando a Message API, pois basta monitorar o evento de troca
d l Pgin 1 do ViewPager para enviar uma mensagem para o outro dispositivo tutor
mando a pgina que ele deve mostrar.
998 wgiz Andina - 4- zio
Pronto, com este simples truque, temos uma galeria de fotos sincronizada entre
o mobile e wear (Figura 3921). Na Figura o efeito da rolagem lateral no ca muito
claro. portanto execute o projeto no emulador do wear e em algum smartphone.
UmaCriando
39.19 va ` pginas
" - . em grid (GridViewPager) 999
' riaaoum
criar do V1ewPager
grid com muito utilizada
linh no wear 0 5,-dv. utilizado ara
1 WPHQHF,
Esse ad f as~e colunas,
, padrao conhecido como 20 Picker P
. `
P' mo de deslgn, tambem referenciado d
Stream* dene que os cards devem Cl- , na Ocumento Oclal P0f(iEXl
Seja necessrio O usurio pode fazer
ser aalicionados
ro agem aralistaa vertical
em uma d' ` ~e -caso.
detalhes
_ _ do
_30cart" .A demonstra
p . ,lrlta
-guraprincipio.
3922 para vlsuahzar
Note
esse ue a nmm ~
e'f'f '9;
deve ir no sentido para baixo e direita. q avegaao
:J V*
19.1
*ri
,ix
_.z
/wear/res/layout/activity_heIIo_gridviewpager.xmI
<androd.support.wearable.vew.BoInsetLayout . . >
<and roid . support . wearable . view . Gridviewliager androd : id="@+d/vewPB9@f "
and roid : i.ayout_wdth=" match_pa rent " android :1ayout_heght="match_parent"
android : keepScreen0n=" true" />
Dica:i neste
._ . O _ ,
</android . support.wearable.vew.BoxInsetLayout>
1 (arrosGridPagerAdapter.java
' z SO
IO
D lc lt QHROWCOUHO { return classicos.size(); }
o ar F " ' ~
xasretornar
as tres colunas (clssicos,
a quantidade esportivos
de linhas e luxo)
do grid que negtgOCa
m
Porem, as imagens dos carros que coloquei no projeto so muito grandes, ento
criei um fragment especco para o carto dos carros. No layout desse carto deixei
apenas o nome e a foto do carro. O Inageview que vai mostrar a foto est configu
rado com o atributo scaleType="tCenter" para redimensionar a foto e centraliz-la
no espao disponvel do carto.
/wear/res/layout/fragment_carro_card.xm|
<LinearLayout android:orientation="vertical" . . >
<Tetvew andr0idzid="@+id/tNome" android:gravity="center"
android:layout_width="match_parent" android:layout_height="wrap_content" />
<mageVew ndr0d;d="@+id/img" android:scaleType="tCenter"
android:layout_width="natch_parent" android:layout_height="match_parent" />
(arroCardFragment.java
public class CarroCardFragnent extends CardFra9\@l l
@0verride
ateContentView(Ly0Uatr "ater' Vewcroup container,
protected View onCre
Bundle savedInstanceState) { t fra ment carro_card, container, falS@);
' ' = inater.inate(R-GYOU ~ 9 _ _
VIEW new view.ndViewById(R - ld - tN'e) '
. . _'view)
z -vtew.ndViewByI
ara id img);
Textvew tNre : (TGXVEW)
N e serrer(9@t^r9Un@S<>-9t5""9(""e l);
-
Imagevtew U'\9 - (Image
in9-setIna9eResource(9@t^f9"'"t5(l'getlm mg W
return view;
}
}
1002 Google Android - 4' edto
zk
fit ) lt tf'
4111, 1_;
L..u~1i1-1
A1...'1
Nota: t .IHi`- llllll HH |llI.tttII.1|t |I||\1{tlI_||| |||1Hl1lIl (I\|',[|'|| Il |/t'|lltI l|||l
tttltlttltltt lt*.|HIl |\1 t' lt'ttIl .1|tI1 |.|I t 1 ltll ttll ttttt |tI111ltI)tIt|| Itllll \
ItIt.11'_11||1 11' It1111|\ lI||||/K 11 .ttl t|\ttt1 1111111ttl;b.1B-q111tI1t1 1l1' 1|1|_||1|1|1~| |_|y.||
. , ioos
Calltulo 39 n Android Wear
Como essa activity desabilitou o gesto padro para fechar o aplicativo, para sair do
aplicativo preciso utilizar a view Dlsmiss0verlayVew, que permite ao usurio tocar
na tela e segurar por dois segundos para fech-la. Ao fazer isso, ser exibido um
alerta de conrmao para 0 usurio. Segundo as boas prticas de interface para
o wear, caso voc utilize este tipo de recurso para fechar a tela, recomendado
informar ao usurio no incio do aplicativo que para fechar a tela necessrio
tocar e segurar por dois segundos.
Ento vamos ao cdigo. No layout da activity basta adicionar um Dsmssoverlayview
por cima de tudo. Lembrando que o layout BoInsetLayout uma subclasse de
V' _ ,, II
FrameLayout, portanto ele vai empilhar as views.
/wear/res/Iayout/activity_full_screen.xmI
<androd.support.wearable.vew.Boxlnsetlayout . . >
an ro : _ '
<TeXatnd1::d'lay0ut width="wFP Cte"t" andrmdzlayout-helght: wrapfontent
`'t
d id layout 9ravi.ty'"center" androd:tet="Tela em Full Screen" />
androd . support.wearable.view.Disnss0verlayView
areia-ia-"@+d/d Vf1aY"
ghdroidilavout wdth="match_parent" androd:lay0U'_h@9h="Watch-par"t />
});
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return nDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
}
O resultado deste exemplo pode ser visto na gura 39.25. Na primeira parte da
'* 1i
gura, podemos ver o alerta inicial que exibido para o usurio ao abrir o apli
cativo pela primeira vez. Logo depois, podemos ver que a aplicao mostrou o
layout da activity normalmente. A terceira parte da gura mostra o alerta que
exibido quando o usurio toca na tela e segura por dois segundos, permitindo
assim que ele feche o aplicativo. Esse padro conhecido como Long press to dismiss_
TdaemFuHScmen
_ _utilizar
g doosmodo
utilizados
de immo e elo seim'l'"`' *M
_
NW d@Pdd0
como
POr
para
padrao
._ gfld
paraQu
em troca, desair
- . , o da tela.
um
um mapa
fechar
' _ p Uesiiau
O gesto
o aplicativ
P pororesabilitaresse
av 1CaV
d de swtpe to dsnss utilizado
~
O po e interferir no funcionament
Justamente POI' 1550, alguns aplicativos o tam d
` d ~ - - gestopadrao*
___mm P melo o padrao Long press to dtsmss.
Conrmando...
Timer
onT|merF|nished!
.. - _ I
View dg tipo De1ayedConrmatonVew no l3Y0Ut'
0 t _ H II
fg /wear/res/layout/act|v|tY_"f"mat'"-delayed Xm
. 1 . `ew.BoInsetLaYUt ' ' > H
<andro1d.support.we.arab e vt . _" horizontal
androd:1aYUt-I
dextvew ' ' cyontent" android:1aYout_h9h'f= wrap-<"t@t
andrmdowydotuhtjlgvirad/ti.
androd:tet="Conrmando..." />
1906 Google Android - 4* edio
android.support.wearable.view.DelayedConrnationView
android:id:"@+id/delayed_conrnation"
android:layout_width="wrap_content" android:layout_height="wfP_Cte"t"
android:layout_gravity="center" android:src="@drawable/C-1a"Chr"
app:circle_color="@color/blue" app:circle_border_color="@color/Ted"
app:circle_radius="@dinen/circle_radius"
app:circle_radius_pressed="@dimen/circle_radius_pressed"
app:circle_padding="@dimen/circle_padding"
app:circle_border_width="@dimen/circle_border_normal_width" />
</android_support.wearable.view.BoInsetLayout>
(onrmationDeIayedActivity.java
public class ConrmationDelayedActivity extends Activity
inplenents DelayedConrnationView.DelayedConrnationListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_conrmation_delayed);
DelayedConrmationView d = (DelayedConrmationView) ndViewById(R.id.delayed_conrnation);
d.setTotalTineHs(5 * 1066); // cinco segundos
d.start();
d.setListener(this);
}
@0verride
public void onTinerFinished(View view) { // usurio no fez nada
Noticationtil.create(this,R.mipmap.ic_launcher,"Timer","onTimerFinished!");
nish();
}
@0verride
}
(apltulo 39 I Android Wear 100 7
No wear, tambm muito comum ver alertas com mensagens de sucesso e erro
quando o usurio executa
a asdetermin d' -esse
aoes. Para criar ` ` tipo
' de alerta.
deve-se adicionar a activity nativa ConrnationActivity no arquivo de manifesto:
<application
<activity
android:nane=" _ . .activity
android.support.wearable ionConrnat`
ctivity"
A` />
</application
No cdi go para criar as mensagens, basta
parardisuma intent com essa
activity O parmetro EXTRAJESSAGE recebe o texto da mensagem e o parmetro
EXTRA_ANIMATION_TYPE corresponde ao tipo do alerta, que pode ser mensagem de
sucesso SUCCESS_ANIMATION, mensagem de erro FAILURE_ANIltATION ou uma mensagem
para indicar que alguma activity foi aberta no smartphone 0PEN_0N_PHONE_ANIMATION.
Na classe llainwea rActivity do projeto Hellowearlews, demonstrei como criar as ani
maes de sucesso e erro, conforme mostra a gura 39.27. A seguir podemos ver
o cdigo que cria essas mensagens.
St'
public void onMessageReceived(nal MessageEvent nessageEvent) {
ring nsg - messageEvent.getPath();
AndroidManifest.mI
<servce androd:name=".He11oLstenerServce"
<ntent-Iter>
<acton androd:name="com.goog1e.androd.gms.wearab1e.BIND_LISTENER" />
</ntent-1ter>
</servce>
(|)\.|l0 39 Andmid wear
@0Ve|-ride rvice {
Public class Helloliste
nerService extends HearableListenerSe '
super.o H '
public void onMessageReceived(MessageEvent nessageEvent) {
} n essageReceived(nessageEvent); // Message API
@0verride
@Override
public void onPeerDisconnected(Node peer) {
super.onPeerDisconnected(peer); // Node API
}
boo ean9etPacka9eMana9er()hS5Y5t@'F@at"(PackagenanagerFEATURE'L0CAT
l h GPS as = `
1010 Google Android - 4 edio
Caso o retorno seja verdadeiro, podemos utilizar a Location API do Google Play
Services, conforme j estudamos, pois o funcionamento idntico ao do smartphone.
Os restantes dos sensores tambm funcionam da mesma forma, sendo assim
podemos utilizar a classe SensorManager, conforme tambm j estudamos.
Sobre este assunto, vale ressaltar que segundo as boas prticas do Google recomen
da-se utilizar o sensor do smartphone sempre que possvel, caso os dispositivos
estejam pareados. Veja mais detalhes a respeito disso na apresentao Wear&Sensors
(Android Performance Patterns) disponvel no canal do YouTube do Google Developers.
Segundo o Google, a justicativa para preferir a utilizao dos sensores do
smartphone para economizar a bateria do relgio, at porque o smartphone tem
uma bateria melhor e um processamento muito maior. Enviar mensagens com
os dispositivos pareados pode custar menos do que ativar os sensores no relgio.
Por exemplo, se voc precisa do sensor de contador de passos possvel utilizar o
sensor do smartphone e enviar o valor para o relgio por meio da Message API.
Porm, caso os dispositivos no estejam pareados, justica-se utilizar diretamente
o sensor do relgio. o caso de um aplicativo de corrida, no qual o relgio pode
ser utilizado para acompanhar a quantidade de passos, batimentos cardacos e
coordenadas GPS sem a necessidade de estar pareado com o smartphone.
http://www.android.com/wear/
Android Developers - Android Wear
https://developer android.com/wear
Android Design - Android Wear
https://developer android.com/design/wear
\"*
cAPruLo 40
Google Play
*-1
app/build.gradle
and roid {
defaultCong {
applcatonld "br.cormlivroandroid.carros"
mnSdkVerson 9 // API Level mnima
targetSdkVerson 22 // API Level tar9t/al-V0 (Sempre 3 ltima)
versonCode 1o dentcador
// Nmer . _ no Goo9l@ Play
da verso
} - , . z ' z lm
versionllane "1 6" // Verso que o usurio vai ver.
Para informaes sobre o nmero de cada API Level, visite esta pgina:
http://developexandroid.com/guide/appendix/api-levels.html
.. , ,__ '`P l1:
Captulo 40 Google Play
Para mlorma`es. 1 . - - ` ou
Q so ore a biblioteca de com patibilidade visite esta '
http.//dr vi Iopenandrord.com/tools/support-library/ 8 na
eNo. _assimmos
ca vtulo `
o ` a-criar. yo certicado
1 38, sobre Gradle, aprendemos e E le'storwe dI
projeto corn esse certificado. Para assinar o aplicativo podemos
_ _ azer o uild pelo ro G dl
utilizar o menu Build >Generate Signed APK ou f b ' - , .
Enm i ' i' ~ - _
temos os dois tipos de builds: debug e release. p pno ra el P015
* 55 e 5 um lembfff, POIS ja aprendemos a fazer isso no captulo 38.
Apenas lembre-seqde
p aue
puar bl'no Google Play deve ser utilizado o cer
icar
ticado de release.
Dica: nunca perca o certificado de release, pois somente com ele possvel
atualizar um aplicativo na loja do Google Play.
Embora seja mais fcil assinar o aplicativo com o Android Studio e o Gradle,
tambm possvel assinar o aplicativo utilizando as ferramentas keytool e jarsgner
disponveis no JDK. Caso precise de mais detalhes sobre isso, recomendo ler a
documentao ocial.
http://developerandroid.com/tools/publishing/app-signing.html
Ser nccesflv Pagar USS; 5OOO para dbnr sua wm,a, . [oe no precisa renovar.
fazer o upload de diversas aplicaoes. O pagamento e unic
Depois que .sua
- , -f `'~z2ublicar
f . uma
_ , no
va.
cont 1 for criada basta entrar no site de administrao do Go0ll
play para visualizar as aplicaoes Ja publicadas ou p
' ~ - v ,` V `
' ~~ ~ llLl[l\~t)
_ _ _ . r ara ublicar um .ipno_Google
A __.
_ _, . ) [111] t..lI'
Nota: o cadastro de dtsenvolvedo 'P PV [o dc crdito nm.nm(,,m1 mia
custa U5$ 50.00, sendo iitetslfl
fazer o pagamento.
1014 Google Android - 4 edio
Na pgina de console do Google Play voc pode clicar no boto Add New Application
para preencher o formulrio com uma breve descrio de sua aplicao e enviar
alguns screenshots. No formulrio, basta fazer o upload do arquivo .apk que
foi assinado, escolher a categoria da aplicao (logo, Finanas, Sade, Esp0rIS,
Educao etc.) e denir se ela gratuita ou paga. O formulrio que voc precisa
preencher simples, e h uma boa documentao de cada campo a ser preenchido,
ento voc no ter diculdades.
Depois de publicar uma aplicao, ela ser disponibilizada quase que instanta
neamente no Google Play e estar pronta para ser instalada.
Lembre-se de que, a cada atualizao da aplicao, necessrio incrementar o
parmetro versonCode no arquivo buildgmdle. Mas a boa notcia que se voc
tentar publicar um apk sem incrementar o nmero da verso, o Google Play vai
avis-lo, ento tudo tranquilo.
Depois de publicar, lembre-se de acompanhar os comentrios e relatrio de erros
enviados pelos usurios, para corrigir eventuais bugs e a m de melhorar seu
aplicativo. Ao atualizar o aplicativo na loja, nunca se esquea de fornecer uma
breve descrio com as melhorias da nova verso, para o usurio ter conhecimento
das novidades.
. uistraafo`' s ul
mamente recomendado assistir o vdeo oi Cl d mg Your mobile apps? eXn.e_
uma rand ' ~' O astasse
ame ur I/0 2
0 500916
nmeros so exibidos Como se n b P S 3 os muito interessantes, grcos e
app .ld e [palestra foi feita sobre o tema, com o ttulo Don't ju tllilllovameme
us1ness.E novamente recomendo que voc assista
o vdeo.a moblle
`
z
Durante a ale
~ `"' :
ca0 confrm ram expllcadas 35 diversas formas de rentabilizar a sua a li
i J 5 i . EE Q
, e a gura 40.1 que foi extrada de um dos slides da apresentalo
.Us iE2E il 1i 3 12
. f 5 "~ 1 1
fPaid downloads Freemium l |n_app purchase if Advemsmg l
4 functionality
itona. qgoodsgwithin ig lon.ads
lthe app 5
...~_. __-.. -7.-_.. --........- .z ,.-
- z-... _-.. _...-._ _ ,l._-_"__
Q _~__
... _... . ___ _._`->.-....
-v`.-,...-~.- ; L -~`-I
_--..E '.,_`_. - ,_`-zl
- `--...4-..v.-..-z.
http://developexandroid.com/tools/publishing/app-signing. html
Conhea tambm
do mesmo autor
9
Fique conectado:
faceboolccom/novatec
D twittencom/novateceditora SW
www.novatec.com.br
8575 2