Você está na página 1de 457

1DUCA0 CltHCI A B TECHOI OCI A w

f i T i i i i i n i i i i i i i i
229623
. 5FCH CAMPUS FQt AUEZAI
1 BIBLIOTECA |
------------ . . . . . . . . . - T
CMPKK
Google Android
para Tablets
Aprenda a desenvolver aplicaes para
o Android - De smartphones a tablets
Ri cardo R. Lecheta
Novatec
Copyright 2012 Novatec Editora Ltda.
Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998.
proibida a reproduo desta obra, mesmo parcial, por qualquer processo, sem prvia autorizao,
por escrito, do autor e da Editora.- ~ ' '
;r-
Editor: Rubens Prates
Reviso gramatical: Alessandra Thom
Editorao eletrnica: Carolina Kuwabata
Capa: DesignCRV
ISBN: 978-85-7522-292-8
Histrico de impresses:
Setembro/2012 Primeira reimpresso
Fevereiro/2012 Primeira edio
Novatec Editora Ltda.
Rua Lus Antnio dos Santos 110
02460-000 - So Paulo, SP Brasil
Tel.: +55 112959-6529
Fax: +55 112950-8869
E-mail: novatec@novatec.com.br
Site: novatec.com.br
Twitter: twitter.com/novateceditora
Facebook: facebook.com/novatec
Linkedln: linkedin.com/in/novatec
OPCE- CAMPUS FORTALEZA!
BIBLIOTECA WU.DYR DIOGO DE SIQUEJ RA f
Registo v. _
P m r ^ Z X E E Z S Z ex..
Oata^i ^L } j2 -3 k . I
O o S~- 2 6
f S S q ,
J L - y . *
D a d o s I n t e r n a c i o n a i s d e C a t a l o g a a o n a P u b l i c a o ( C I P )
( C m a r a B r a s i l e i r a d o L i v r o , S P , B r a s i l )
L e c h e t a , R i c a r d o R.
G o o g l e A n d r o i d p a r a t a b l e t s : a p r e n d a a
d e s e n v o l v e r a p l i c a e s p a r a o A n d r o i d : d e
s m a r t p h o n e s a t a b l e t s / R i c a r d o R. L e c h e t a .
S o P a u l o : N o v a t e c E d i t o r a , 20 1 2 .
B i b l i o g r a f i a .
I S B N 9 7 8 - 8 5 - 7 5 2 2 - 2 9 2 - ;
1. A n d r o i d ( P r o g r a m a d e c o m p u t a d o r ) 2. A p l i c a o
d e p r o g r a m a - D e s e n v o l v i m e n t o 3. C o m p u t a o m v e l
4. G o o g l e 5. I n t e r n e t s e m f i o 6. T a b l e t s I. T t u l o .
C D D - 0 0 5 .26
n d i c e s p a r a c a t l o g o s i s t e m t i c o :
1. A n d r o i d : p l a t a f o r m a d e d e s e n v o l v i m e n t o p a r a
a p l i c a t i v o s m v e i s : T a b l e t s s P r o g r a m a d e
c o m p u t a d o r 0 0 5 . 2 6
V C 2 0 1 2 0 9 0 3 ,
Este livro dedicado a Deus, a toda a minha famlia e principalmente aos
meus pais, por terem me dado todo o carinho e a melhor educao possvel e
por serem um grande exemplo de boas pessoas.
Em especial, este livro dedicado Paty, que uma pessoa muito especial
em minha vida e que sempre esteve ao meu lado me apoiando em todos os
momentos. Paty, voc sabe que s fico completo quando estamos juntos e que
voc a dona do meu corao. Te amo.
Sumrio
i
Cj Cj j HJ ^ o
Agradeci men tos...............................................................................................................................................13
Sobre o autor................................................................................................................................................... 14
Pref ci o.............................................................................................................................................................. 15
Cap tulo 1 In troduo.................................................................................................................................16 ; <
1.1I ntroduo.......................................................................................................................... 16 !
1.2 Desenvolvendo desde smartphones at tablets........................................................16 ; >-
13 Android 3.x - Honeycomb.............................................................................................17 !o o I
1.4 Android 4.x - Ice Cream Sandwich............................................................................. 17 1^ ^ 1
1.5 Organizao do livro.......................................................................................................19 ? j g'
\ % %
Cap tulo 2 Apli cati vo de exemplo........................................................................................................... 23 g ^
2.1 O projeto.............................................................................................................................23 i J j. ^
2.2 Criando o projeto....................................................................................................................26 !
23 Criando a tela principal - Dashboard.................................................................................29
2.4 Criando os layouts de cabealho e rodap........................................................................ 35
2.5 Adicionando os eventos nos botes do dashboard.........................................................38
2.6 A classe Carro........................................................................................................................... 40
2.7 Arquitetura de integrao com o legado............................................................................ 41
2.8 Requisio HTTP para ler o arquivo XML........................................................................42
2.9 Criando um projeto biblioteca para o projeto..................................................................49
2.10 Criando testes unitrios com J Unit..........................................................................53
2.11 Verificando se existe conexo de dados disponvel............................................... 58
2.12 Trabalhando com threads............................................................................................ 61
2.B A classe AsyncTask........................................................................................................63
2.14 Criando a interface para a lista de carros com um ListVievv.............................67
2.15 Criando o adapter para a lista de carros.................................................................69
2.16 Encapsulando o AsyncTask para tornar o cdigo mais simples.........................81
2.17 Exibindo os detalhes do carro selecionado............................................................ 85
2.18 Exibindo a tela de sobre com um WebView...........................................................92
2.19 I ntegrando J ava e J avaScript no WebView.............................................................. 94
2.20 Exibindo um ProgressBar enquanto oWebView no carrega......
^ Google An droi d para T ablets
Captulo 3 Controlando o estado de sua activity.................................................. ..............101
3.1 Vertical e horizontal........................................................................................................ 101
3.2 Forando a tela a trabalhar na orientao desejada.............................................. 102
33 Criando telas diferentes para a vertical e a horizontal.......................................... 105
3.4 O problema: o Android destri e recria a activity ao trocar de orientao.... 111
3-5 Salvando o estado da tela onSaveI nstanceState()................................................113
3.6 Salvando o estado da tela - onRetainNonConfigurationI nstance()...............117
3.7 Salvando o estado da tela Qual mtodo utilizar?................................................118
3.8 Fazer cache das imagens para melhorar a performance................................. 122
3.9 Alternando o layout manualmente sem a necessidade de destruir e recriar a
tela - androidxonfigChanges......................................................................................126
3.10 O problema com a configurao do androidxonfigChanges........................... 129
3.11 Smartphones com teclado............................................................................................130
3.12 A tela de aguarde durante a busca dos carros.........................................................BI
Captulo 4 Trabalhando com diversos tamanhos de tela........................................................137
4.1 I ntroduo.........................................................................................................................D7
4.2 Exemplo prtico sobre o problema de resoluo...................................................138
43 Termos e notaes utilizadas........................................................................................143
4.4 Convertendo pixels em dp e vice-versa.................................................................... 150
4.5 Customizando as imagens conforme a densidade da tel a...................................155
4.6 A tag <supports-screens> no AndroidManifest.xml.............................................156
4.7 A ordem que o Android utiliza para buscar os prefixos.......................................159
4.8 Mantendo a compatibilidade com Android 1.6 ou superior...............................160
4.9 Mantendo a compatibilidade com Android 1.5 ou superior............................... 161
4.10 Melhores prticas para a criao de telas................................................................163
4.11 Aplicativo dos carros customizado para tela grande............................................163
4.12 I nstalando um add-on para simular o GalaxyTab............................................... 164
4.13 Customizando a tela para o GalaxyTab, tela =large............................................165
4.14 O problema de duplicar o cdigo..............................................................................173
Captulo 5 Android 3.x para tablets"Honeycomb".................................................................174
5.1 I ntroduair..........................................................................................................................174
5.2 Desenvolvendo aplicaes para o Android 3.x........................................................ 176
53 Criando um Emulador para Android 3.x.................................................................176
5.4 Executando a aplicao dos carros no Android 3.x............................................... 178
5.5 Preparando o projeto para Android 3.x.....................................................................181
5.6 Customizando a ActionBar para Android 3.x ou superior...................................186
5.7 I dentificando se um tablet com Android 3.x........................................................187
Sumri o
9
Captulo 6- Fragments............................................................................................................192
6.1 I ntroduo.......................................................................................................................192
6.2 O problema ao reutilizar views em diversas activities..........................................193
63 Como controlar o contedo de diversas views e diversas threads em paralelo..
196
6.4 Criando o projeto com Android 3.x.......................................................................... 197
65 Alterando o exemplo para utilizar fragments........................................................202
6.6 Ciclo de vida de um fragment....................................................................................212
6.7 Exemplo para debugar o ciclo de vida de um fragment......................................216
6.8 Buscar um fragment na tela............................................................................ ........220
6.9 Criando os fragments dinamicamente com FragmentTransaction...................224
6.10 FragmentTransaction - mais alguns exemplos.................................................... 227
6.11 Fragment back stack...... ............................................................................................ '230
6.12 Biblioteca de compatibilidade...........................................:....................................230
6.13 Migrando o projeto dos Fragments para utilizar a biblioteca de compatibili
dade......................................7...........................................................................................231
6.14 Detalhes sobre o ciclo de vida de um Fragment..................................................236
6.15 O mtodo setRetainlnstance(boolean).................................................................. 238
6.16 Ciclo de vida de um FragmentTransaction...........................................................239
Captulo 7 - ActionBar...............................................................................................................243
7.1 I ntroduo.........................................................................................................................243
7.2 Preparando o projeto de exemplos............................................................................244
73 Adicionando itens de menu via API ..........................................................................244
7.4 Adicionando itens de menu via XML.......................................................................246
75 Utilizando'o cone do aplicativo como a home......................................................248
7.6 Trabalhando com Tabs..................................................................................................253
7.7 I nserindo uma view customizada na barra.............................................................258
7.8 Mtodos utilitrios da ActionBar..................... .........................................................263
7.9 ActionBar em verses anteriores ao Android 3.x...................................................264
V
Captulo 8 * Migrando o aplicativo para tablets.......................................................................267
8.1 I ntroduo.......................................................................................................................267
8.2 Criando o projeto.......................................................................................................... 272
83 Analisando a metodologia utilizada para a migrao.........................................273
8.4 Adicionando a biblioteca de compatibilidade no projeto...................................274
8.5 Criando a classe base para os fragments................................................................. 275
8.6 Criando o fragment para o dashboard.....................................................................278
8.7 Criando o fragment para a tela de sobre.................................................................282
8.8 Dividindo a tela do dashboard em duas partes nos tablets...............................285
8.9 Tratando o layout diferenciado para tablets.........................................................-289
8.10 Criando o fragment para a tela de detalhes do carro......................................... 291
10
Google An droi d para T ablets
8.11 Criando o fragment para a tela de listagem de carros....................................... 295
8.12 Criando o layout para a listagem de carros para tablets...................................299
8.13 Utilizando o fragment de detalhes na direita.......................................................302
8.14 Navegao por Tabs na ActionBar para tablets....................................................305
8.15 Criando os itens de menu na ActionBar.................................................................312
8.16 I mplementando a ao do item atualizar na ActionBar.....................................315
8.17 I mplementado a busca de carros com o SearchView..........................................319
Captulo 9 Android 4.x - IceCreamSandwich....................................................................... 325
9.1I ntroduo.........................................................................................................................325
9.2 Configurando o emulador do Android 4.x..............................................................325
93 Android 4.0 ICS possui API Levei = 14.....................................................................327
9.4 Executando a aplicao no Android 4.x I CS...........................................................328
95 Verificando as outras telas da aplicao no Android 4.x I CS............................. 331
9.6 Customizando o texto da ActionBar......................................................................... 332
9.7 Utilizando o menu fsico para acionar os itens de menu da ActionBar.........334
9.8 Troca de orientao na verso smartphone com Android 4.x I CS....................336
9.9 Travando a tela na horizontal para os tabl ets.........................................................339
Captulo 10* Animaes............................................................................................................341
10.1 A classe Animation........................................................................................................341
10.2 Criando o projeto para as animaes..................................................................... 343
103 AlphaAnimation...........................................................................................................348
10.4 RotateAnimation...........................................................................................................351
10-5 ScaleAnimation............................................................................................................ 356
10.6 TranslateAnimation..................................................................................................... 359
10.7 A nimationSet................................................................................................................362
10.8 AnimationListener.......................................................................................................366
10.9 LayoutAnimationController...................................................................................... 370
10.10 I nterpolator................................................................................................................... 371
10.11 VievvFlipper...................................................................................................................373
10.12 View customizada com animao ..........................................................................376
10.13 I nserindo uma animao no projeto dos carros................................................. 378
Captulo 11 * Animaes comAndroid 3.0.................................................................................382
11.1O problema com a API de animaes no Android 2.x........................................ 382
11.2 Property Animation - A animao criada a partir do Android 3.x.................388
113 A classe ValueAnimator...............................................................................................389
11.4 A classe ObjectAnimator.............................................................................................393
113 ObjectAnimator - Criando uma animao alpha ............................................... 3^5
11.6 ObjectAnimator - Girando um objeto................................................................... 397
11.7 ObjectAnimator-Scale............................................................................................... 398
Sumrio 11
6
11.8 ObjectAnimator- Movendo um objeto pela tel a...............................................399
11.9 Criando um conjunto de animaes com a classe AnimatorSet.....................401
11.10 AnimatorListener....................................................................................................... 402
11.11I nserindo uma animao no projeto dos carros.................................................406
Cap tulo 12 Google An alyti cs e Google Mobi le Ads........................................................................ 410
12.1 I ntroduo ao Google Analytics...............................................................................410
12.2 Criando uma conta no Google Analytics..............................................................411
123 Fazendo o download do SDK do Google Analytics............................................413
12.4 Criando os pagevievvs................................................................................................ 416
12.5 Visualizando os relatrios.........................................................................................418
12.6 Introduo ao Google Mobile Ads - AdMobs..................................................... 420
12.7 Google Mobile Ads..................................................................................................... 421
12.8 Registrando a aplicao no AdMobs......................................................................422
12.9 Configurando o projeto............................................................................................426
12.10 Como adicionar um anncio com o AdView.....................................................428
12.11 Alterando os layouts das telas de carros..............................................................430
12.12 Verificando os logs.....................................................................................................432
12.13 Voltando administrao da AdMobs...............................................................433
12.14 Configurando um banner para a verso Tablet..................................................433
Cap tulo 13 Google T V............................................................................................................................ 438
13.1 I ntroduo.....................................................................................................................438
13.2 Preparando o ambiente de desenvolvimento........................................................439
133 I nstalando o KVM......................................................................................................439
13.4 I nstalando o Google TV Add-On........................................................................... 440
13.5 Executando o emulador do Google TV ................................................................442
13.6 I nstalando a aplicao dos carros.......................................................................... 443
13.7 Mais sobre o Google TV............................................................................................445
n di ce remi ssi vo............................................................................................................................................447
Agradecimentos
Este livro no teria acontecido sem a ajuda de diversas pessoas, algumas pela
contribuio tcnica, outras pela motivao.
O principal agradecimento sem dvida a toda a comunidade de desenvol
vedores Android que ajudam de alguma forma esse mercado de mobilidade a
crescer e a todos que me enviaram feedbacks para que esta segunda obra pudesse
ser realizada.
Agradeo a todo o pessoal da Wasys e Livetouch pelos incrveis projetos de
mobilidade e a todo o pessoal tcnico da equipe pelas ideias e sugestes.
Em especial, agradeo ao Rubens Prates e Ana Carolina Prates, editores da
Novatec, por toda a calma e pela orientao em todas as etapas da produo
deste livro. Seus conselhos foram fundamentais para que tudo isto acontecesse.
Por ltimo, agradeo a voc pelo interesse em ler esta obra. Espero que a leitura
seja simples e empolgante.
1 13
Sobre o autor
Ricardo R. Lecheta formado em Cincia da Computao e ps-graduado em
Gesto do Desenvolvimento de Software pela PUCPR.Tem certificaes da Sun,
IBM e Rational, entre elas SCMAD (J 2ME) e SCEA (arquiteto).
Atualmente trabalha como consultor e desenvolvedor mobile para diversas
plataformas e pode ser contatado pelo email rlecheta@gmail.com.
Prefcio
Atualmente, diversas pesquisas apontam o Android como o sistema operacional
para smartphones e tablets que mais cresce no mundo. Sua arquitetura simples e
flexvel permite que ele seja adotado por diversos fabricantes no mundo inteiro,
que se beneficiam da sua base slida para criar produtos incrveis e diferenciados
em um mercado que est em plena ascenso e extremamente competitivo.
Hoje em dia temos smarqDhones com processadores de um computador, os gran
des tablets e ainda novas plataformas, como o Google TV A evoluo da tecnologia
grande, e a disputa no mercado de mobilidade est acirrada, de maneira que
mesmo especialistas no assunto tm dificuldades de acompanhar esse movimento.
O objetivo deste livro auxiliar o leitor a acompanhar esse mercado, com uma
abordagem simples e que vai direto ao ponto. Para a compreenso do texto so
necessrios conhecimentos de bsicos a intermedirios de Android.
Nosso objetivo criar um projeto Android, passo a passo, apresentando diversas
dicas, boas prticas, e abordando os problemas mais comuns no desenvolvimento.
Conceitos fundamentais sobre como criar aplicaes que funcionem de forma
correta em diversas telas e resolues, dos smartphones aos grandes tablets, tra
tar corretamente as telas na vertical e horizontal, integrao com servidores web
com requisies HTTP para buscar informaes, processamento em background,
detalhes de animaes e os novos recursos disponveis na API sero apresentados
de forma prtica em nosso projeto.
A cada novo captulo, medida que novos conceitos so explicados, o projeto
vai evoluindo junto, passo a passo, e no final da leitura estar criada uma aplica
o compatvel, desde os smartphones com Android 1.6 at os novos tablets com
Android 3.x, e claro que o Android 4.x ICS no ficar de fora.
Os conceitos apresentados tambm so a base do Android e valem para qual
quer verso da plataforma. O sistema operacional do robozinho verde no para
de crescer e com certeza aipda vai nos surpreender muito com sua evoluo. Este
livro vai fornecer a voc uma boa base para acompanhar esse mercado.
Conhea o aplicativo que vamos desenvolver em www.livroandroid.com.br/carros.
15
CAPT ULO 1
Introduo
1.1 I ntroduo
Atualmente, diversas pesquisas apontam o Android como o sistema operacional
para smartphones que mais cresce no mundo. Sua arquitetura simples, flexvel e
ao mesmo tempo poderosa permite com que ele seja a base para muitos produtos,
que se beneficiam de sua plataforma.
A disputa no mercado de mobilidade est extremamente acirrada, com diversas
inovaes e lanamentos acontecendo em todos os lugares, e mesmo os especia
listas sentem dificuldade em acompanhar tamanha evoluo.
Estamos na dcada da mobilidade, onde smartphones e tablets faro cada vez
mais parte de nosso dia a dia, e o mercado busca incessantemente por especialistas
no assunto para desenvolver aplicativos comerciais e coorporativos para os mais
diversos setores, como varejo, sade, economia, jogos e muito mais.
O objetivo deste livro fornecer uma base slida para que se acompanhe esse
mercado e que se desenvolvam aplicaes competitivas com o simptico sistema
operacional do robozinho verde.
1.2 Desenvolvendo desde smartphones at tablets
Para que se compreenda este livro recomendam-se conhecimentos bsicos de
Android, para que se consiga acompanhar os exemplos.
Nosso objetivo desenvolver, passo a passo, uma aplicao que funcione
desde nos smartphones com Android 1.6 at nos novos tablets com Android 3.x
Honeycomb.
16
Capi tulai In troduo 17
Recentemente tambm foi lanado o Android 4.x Ice Cream Sandwich, o qual
visa unir as plataformas de desenvolvimento dos smartphones e tablets e trazer
as novas APIs, que inicialmente estavam disponveis apenas no Honeycomb para
os smartphones.
Com todas essas verses do Android e toda a diversidade de aparelhos com
diferentes tamanhos de tela e resoluo, surge uma dificuldade em desenvolver
aplicativos compatveis com todos eles.
Nesta obra, a cada captulo vamos evoluir nosso projeto, utilizando vrias
boas prticas e dicas de desenvolvimento medida em que os assuntos vo sendo
abordados.
Como foco principal temos o desenvolvimento de uma aplicao nica para
smartphones e tablets, e ainda usufruindo de novos recursos, como Fragments e
ActionBar, disponveis a partir do Android 3.x - Honeycomb.
1.3 Android 3.x - Honeycomb
Com a popularizao dos tablets e a grande busca dos usurios por esses equi
pamentos surgiu a necessidade de otimizar e customizar o Android para que se
usufrua ao mximo do tamanho de tela disponvel nesses aparelhos.
A resposta do Google para essa demanda foi o lanamento do Android 3.x,
conhecido como Honeycomb, com o objetivo de.fornecer ao usurio uma tima
experincia ao utilizar os tablets.
Durante a leitura vamos estudar as novas APIs disponveis a partir do Ho
neycomb, como Fragments e ActionBar, e otimizar o projeto do livro para tablets.
A figura 1.1 exibe a aplicao dos carros executando em um tablet.
1.4 Android 4.x - Ice Cream Sandwich
Depois que o Android 3.x =-Honeycomb foi lanado, com novos recursos e fun
cionalidades, surgiu uma grande necessidade de unificar as plataformas de desen
volvimento entre os tablets e smartphones. Para isso foi lanado o to aguardado
Android 4.x - Ice Cream Sandwich (ICS), que unificou o sistema operacional,
permitindo que os smartphones pudessem utilizar os ltimos recursos e APIs
disponveis no sistema operacional.
AFerrari ff xjJw der revetaU. $* irjiadoprimeiromodelod* mira i ter traJomiegrjl. Almdujo. eletontacomummotor
dianteiroVU. S* i deummodeloGTdequatrolugiresquenio l itrUKul a612mis timUematril umnovotipoCetiitme.
daquelequegosudepercorrer ummhosmaisCillcei queexigemJ j Integra.
e modelorevoJuaoMnp(Centro6a marca) (emumnovocfwsycomerire-euos maior, jlmCeusper.woindependenteque
incorpcr j ajttimj gerjio deamortecedoresfcUveii, almdeheiot decerimicad* Brembo.
j&&gad
Figura 1.1 - Projeto dos carros executando em um tablet com Android 3.x - Honeycomb.
A figura 1.2 exibe a aplicao dos carros funcionando em um smartphone com
Android 4.x - ICS.
LUXO ESPORTIVOS
' Livro Android - Todos os direitos rtiwvados
Figura 1.2 - Projeto dos carros executando em um smartphone com Android 4.x - ICS.
Cap tulo 1 In troduo
[
1.5 Organizao do livro
Esta obra dedicada aos desenvolvedores Android que j desenvolvem aplica
tivos para esse sistema operacional, ou pelo menos conhecem o bsico sobre a
plataforma.
O desenvolvimento do projeto j comea a todo o vapor no captulo 2 e com
uma abordagem simples e direta. Para comear construda, passo a passo, uma
aplicao que lista diversos carros separados por categoria, com todos os detalhes
necessrios para um bom entendimento do exemplo.
Assuntos avanados, como tratamento de threads, requisies http, parser de
xml e at como criar projetos de biblioteca com classes reutilizveis e um projeto
de testes, so explicados e detalhados j no captulo 2.
A figura 13 exibe o projeto que teremos executado no final do captulo 2.
No captulo 3 explicado como o Android trata a troca de orientao, onde
vamos estudar como criar telas especficas para a vertical ou a horizontal, deta
lhes importantes sobre o ciclo de vida das aplicaes e como salvar o estado das
informaes corretamente durante a troca de orientao.
O captulo 4 explica a fundo o maior problema que temos com o Android hoje,
que o fato de existirem diversos aparelhos com diferentes tamanhos de telas e
resolues. Existem celulares pequenos, grandes e tablets, que no se diferenciam
somente pelo tamanho das telas, mas tambm pela sua resoluo. Nesse captulo
vamos estudar todos os detalhes que voc precisa saber para dominar esse assunto,
para que seja possvel criarmos aplicaes que funcionem em diversos tamanhos
de tela. Esse um captulo bem terico e apresenta uma base muito forte que
todos os desenvolvedores Android precisam dominar.
O livro muito prtico, e os detalhes para tratar as telas na horizontal e criar
layouts diferenciados, assim como aspectos avanados do ciclo de vida da apli
cao, so abordados de forma simples.
No captulo 5 seguimos com nossa abordagem, e agora o prximo passo
ser a introduo ao Android 3.x - Honeycomb, onde discutiremos aspectos
importantes que devem ser levados em considerao ao migrar as aplicaes de
smartphones para tablets.
Os captulos 6 e 7 fornecem uma base slida para as novas APIs de Fragments
e ActionBar, criadas para auxiliar o desenvolvedor no aproveitamento do grande
espao disponvel nas grandes telas dos novos tablets de 10, assim como ajudar
na organizao e manuteno do cdigo.
20
Google An droi d para T ablets
*
$ C 4 0 /
Ciirci S h o w u t e
Uvro
Google
Android
Esportivo*
Bte livro dedtodo *s
desenvolvedores Android que
desejamaprimorar seus
conhecimentos e estudar as novas
fundona&Jdes disponveis t>o
Android Xx. como fragments e
ctionbar.
. tf* 4 07
-
y err s * o6|
iV ,2
1
^
%'y.
l?2i\X,-j3
f- TuckerlM
itiicifas&cc-si--:
.... C ar r os, Sf towtasev. - . ;
O Tucfcer (oJ reaJ mftt uma tnovaJ o no
mundo do drslptautomfrtie embora o
modeto 1MS foi o nico modeto)! produzido
seu efefco sobre o mundo dosautomvebaMa
pode r sendda t hoje. Preston Tixter e Alex
Trcrmfe proletouoTuchcr como uma ttraoa
de entrar na Inttarta automotha. e apesar de
apenas um ptmhad de carros foram
produzidos o* recursos Que ettavam pmtrcti
naqueles carros ram extremamente inovador
paraaepoca.
Tc&f tHaW-^rtttrOf'
Figura 1.3 -Aplicao dos carros para smartphone Android 2.x.
O captulo 8 onde comeamos a migrao da aplicao dos carros para tablets. O
resultado depois da utilizao dos novos conceitos pode ser visualizado na figura 14.
No final desse captulo a aplicao estar pronta e funcional, e teremos em um
nico build uma aplicao que funciona desde em smartphones com Android
1.6 at nos novos tablets com Android 3.x. Alm disso, exibido um layout dife
renciado para cada tipo de tela, para que se aproveitem ao mximo os padres e
recursos disponveis na plataforma.
Cap tulo 1 In troduo 21
en M W tf in t ts Trophy
y 3 OnsnlB
ll n t f i l i Srsnubrto Spart
Dtxnfto
Figura 1.4 - Aplicao dos carros executando em um tablet com Android 3.x de 10.
No captulo 9 feita a introduo ao Android 4.xICS, e vamos verificar como
a aplicao construda para tablets pode executar em um poderoso smartphone
que vai usufruir dos novos recursos, inclusive da navegao porTabs na ActionBar.
Esta obra no para por a, e os captulos 10 e 11 explicam as bibliotecas dispo
nveis para animao no Android 2.x e o novo framework de animao criado a
partir do Android 3.x, respectivamente. Diversos exemplos simples e prticos so
criados para explicar as animaes, e no final desses captulos vamos turbinar a
aplicao dos carros e criar alguns efeitos especiais.
No captulo 12 vamos estudar como exibir anncios do Google Mobile Ads -
AdMobs na aplicao e a possibilidade de ter lucro com o aplicativo sempre que
algum usurio clicar sobre um anncio.
Outro assunto que vamos estudar como utilizar o Google Analytics para
monitorar as telas acessadas da aplicao, assim como diversos eventos gerdos,
para descobrir como o usurio est utilizando tal aplicao e posteriormente ter
acesso a interessantes relatrios e mtricas.
A figura 1.5 mostra os anncios sendo exibidos na aplicao dos carros.
22
Google An droi d para T ablets
t-vW - LCirrosShowcase
Clssicos Luxo
Esportivos sobre
Celulares e Smartphones .
Dtorsci WoJ ckHv e Mj i mi eom os Melhctn
P/ros Confiro OfcftK! '
v / : ' . .
Carros Showcasetr
| Lamborgmm Avenudor /
Chevrolet Corvette 236 >
BMW MS > a
Renault Megane RS Trophy >
Maserati GraneabrJ o sport >
McLa r en MP4-12C >
MERCEDES-BENZ C63AMG >
Tablets no Magazine lulza .
Tjtfrtl Antrol- wn j(< 12* fnlrr <
Ap*-*v<'t.MaW.eimw
Figiira 1.5 - Anncios sendo exibidos na aplicao.
Para finalizar, no captulo 13 vamos verificar como a aplicao executa no
emulador da Google TV e dar um primeiro grande passo para desenvolver para
essa plataforma. A figura 1.6 exibe a aplicao dos carros na Google TV
* Figura 1.6 - Google TV.
O objetivo deste livro criar uma aplicao, que ser desenvolvida passo a
passo juntamente com voc, a cada captulo. Boa leitura.
(
CAPT ULO 2
Aplicativo de exemplo
Neste captulo vamos desenvolver, como exemplo, um interessante projeto em que
diversos conceitos usados em aplicaes reais sero utilizados.
Nessa aplicao vamos exibir diversos carros em uma lista, com descrio e
foto. Ao selecionar um carro na lista, vamos exibir outra tela com os seus detalhes.
Nesse projeto vamos fazer uma requisio HTTP em um servidor web para |
buscar um XML com todos os carros disponveis e validar importantes conceitos i
no desenvolvimento. (
i
Posteriormente, vamos melhorar esse projeto para utilizar novos recursos dos i
tablets Android e apresentar conceitos importantes que devem ser levados em
considerao ao desenvolver aplicaes.
; ,j..i
:
;
2.1 0 projeto :
O projeto que vamos desenvolver simples, mas demonstra uma srie de padres
e tcnicas de desenvolvimento utilizadas no Android. Muitos conceitos sero
apresentados, e cada captulo ir explicar diferentes aspectos da aplicao, de
forma que no final do livro teremos construdo uma aplicao completa para
tablets com Android 3.x ou superior.
Na primeira tela do aplicativo podemos visualizar um menu com as opes,
conforme a figura 2.1. Esse menu conhecido como dashboard e um padro
bastante utilizado em aplicaes Android.
No dashboard possvel escolher entre visualizar a lista de carros clssicos,
luxo ou esportivos, o que vai buscar uma lista de carros na internet e exibir os
detalhes, conforme as figuras 2.2 e 23.
23
p
u
s

;
:

U
A
i

2
A
i
24
Google An droi d para T ablets
t i!l! r. 5:32
Carros Showcase,
Esportivos Sobre
Figura 21 - Tela inicial do aplicativo.
Carros Showcase ...
O
Ferrari FF
O
AUDI GT Spyder
G
Porsche Panamera
C
Lamborghini Aventador
A
Chevrolet Corvette Z06
BMW M5
e s!t! V 5 3 5 I ? * e ill 93 5*36
;.
Carros Showcase.
AUDI GTSpyder
Porsche Panamera
Lamborghini Aventador
Chevrolet Corvette Z06
Sfc BMW M5
Renault Megane RS Trophy > f^^A- ^j RenauI tMeBan^STroDW
Smartphones e celulares -'-A1?,
LG, Semp Toshiba, Samsung em Vrios Modelos. 1^
Compre em At 7x Sf J uros! '' *
' LivroAndroid - Tods s direitos reservados' ^ ,
Figuras 2.2 e 2.3 - Lista de carros sendo buscadas dinamicamente na web.
Depois que temos a lista de carros podemos selecionar um deles e visualizar os
detalhes, conforme a figura 2.4. Nessa tela exibida uma descrio sobre o caro e
um boto que, se pressionado, vai abrir o site do fabricante, conforme a figura 2.5.
Cap tulo 2 Apli cati vo de exemplo
25
11 tit^y/www.fCTrari.coW... jj- j j ;.
A Ferrari FF acaba de ser revelada. Se trata do
primeiro modelo da marca a ter trao integral.
Alm disso, ele conta com um motor dianteiro
VI 2. Se trata de um modelo GT de quatro
lugares que no s substitui a 612 mas tambm
atrai um novo tipo de cliente, daquele que gosta
de percorrer caminhos mais difceis que exigem
trao integral.
ste modelo revolucionrio (dentro da marca)
tem um novo chassi com entre-eixos maior,
alm de suspenso independente que incorpora
a ltima gerao de amortecedores ajustveis,
alm de freios de cermica da Brembo.
Figuras 2.4 e 2.5 - Detalhes do carro selecionado.
No dashboard tambm podemos pressionar o boto Sobre, que vai abrir um
WebView, conforme a figura 2.6.
Li vro
Googl e
Androi d
Googa
AflDROI D
Este livro dedicado aos
desenvolvedores Android que
desejam aprimorar seus
conhecimentos e estudar as novas
funcionalidades disponveis no
Android 3.x, como fragments e
actionbar.
i/ -^ livrAridrotd Todos os direitos reservados' - * ;
Figura 2.6 - Tela de Sobre com WebVtew.
No decorrer do livro vamos migrar a aplicao dos carros para tablets com
Android 3.x, explicando todos os detalhes necessrios para fornecer uma base
26
Google An droi d para T ablets
slida no desenvolvimento de aplicaes Android. A figura 2.7 exibe a aplicao
final executando em um tablet.
Chevretet Bel-Air
i OTixkct foi lejlmentt umainovalo no mundo do deogn auiomvet. eembora o modelo o nico modtlo p proflujida seu
: efeito sobreomundo dos auiomoveis amda pode ser sentida atho|e. f i eston Tutker e AIez Tremulis pro|eiau o Tutker como uma
i tentaitva de entrar naindtna automotiva. e apesar de jperus umpunhado de carros (ciamprodtuidos os recursos que eitavam
| presentes naqueles carros eramextremamente inovador parapoca.
iiSflaggJ
m a m w M & r ~ S
Figura 2.7 -Aplicao dos carros sendo executada em um tablet com Android 3.x.
Essa a proposta do livro, apresentar passo a passo o desenvolvimento de uma
aplicao ao mesmo tempo em que os novos conceitos vo sendo explicados, cada
um ao seu tempo.
Para compreender o livro voc precisa ter conhecimentos de Android, pelo
menos o bsico, porque o principal objetivo apresentar tcnicas de design e
novos conceitos de desenvolvimento para tablets.
2.2 Criando o projeto
Depois de entendermos a aplicao que vamos construir, chegou o momento de
colocarmos a mo na massa e partir para a prtica.
Para iniciar este captulo vamos preparar o projeto de exemplo.
Nome do projeto: LivroAndroid-Cap02-Carros
Verso: Android 23.x
Pacote: br.livroandroid.carros
Criar activity classe = M ain.j ava
Verso mnima do SDK: 4 (Android 1.6)
Se voc preferir, pode importar no worspace o projeto LivroAndroid-Template
como base para acompanhar os exemplos. Esse projeto tem a estrutura de
pastas j criadas e contm todas as imagens que vamos utilizar.
Como nosso projeto vai acessar a internet, vamos adicionar a devida permisso
no AndroidManifest.xml.
< uses-permission android:name= " android.permission.INTERNET" />
Lembre-se que as permisses sero exibidas ao usurio no momento de instalar
o aplicativo pelo Android Market (Google Play) e tm como objetivo alert-lo de
que sua aplicao pretende acessar o recurso X ou Y
Feito isso, vamos adicionar um tema customizado no nosso aplicativo. Portanto,
adicione o atributo android :theme no AndroidManifestxml, conforme demonstrado a seguir.
opplication android:icon= ,'@drawable/icon" android:label= @string/app_naine''
android:theme= " @ sty le/tema>
No final dessas alteraes o arquivo de configurao deve ficar desta maneira:
i f t An droi dMan i f est.xml
<? xml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= http://schemas.android.com/apk /res/android"
pack age= " br. livroandroid. carros"
android:versionCode= l"
android:versionName= " l.0">
<uses-sdk android:minSdk Version= " 4" />
< uses-permission android:name= " android.permission.INTERNET" />
opplication android:icon= " @ drawable/icon" android :label= " @ string/app_ name"
android:theme= @ sty le/tema" >
<activity android:name= " .M ain" android:label= "@ string/app_narae">
<intent-filter>
<action android:name= android.intent.action.H AIN/>
<category android:name= " android.intent.category .LAU NCH ER" />
</intent-filter>
</activity>
</application>
</manifest>
A partir do ADT 14.0.0 o plugin do Eclipse customiza no arquivo
AndroidManifest.xml o nome do cone da aplicao para android :icon="@
drawable/ic_launcher, em vez de android:icon="@ drawable/icon", como
anteriormente. Neste livro utilizaremos a notao antiga, e a imagem icorupng
utilizada nos exemplos pode ser encontrada no link de download do livro.
Cap tulo 2 Apli cati vo de exemplo
jUK-iwjisr,',
28
Google An droi d para T ablets
Feitas essas alteraes, o projeto no vai compilar, pois estamos utilizando
um tema que ainda no foi criado. Portanto, vamos criar o arquivo de. estilos Iresl
values/css.xml, conforme demonstrado a seguir.
* /res/values/css.xml
< resources>
< ! Tema padro -->
< sty le name=" tema" parent= android:sty le/Theme.Light" >
< item name= ''android:windowNoTitle'> true< /item>
< /sty le>
<! -- Tex to com Fonte 12 -->
< sty le name= " tex tol2" >
< item name= " android:tex tColor" > @ color/preto< /item>
< item name= " android;tex t5iz e" > 12sp< /item>
</sty le>
< sty le name= " tex tol2Negrito" parent= " tex tol2" >
< item name= " android:tex tSty le" > bold< /item>
< /sty le>
<! -- Tex to com Fonte 14
< sty le name= " tex tol4" >
< item name= ' ' android:textColor" >gcolor/preto</item>
< item name= " android:tex tSiz e" > 14sp< /item>
< /sty le>
< sty le name= " tex tol4Negrito' parent= tex tol4>
< item name= "android:tex tstyle">bold</iteni>
< /sty le>
< /resources>
Esse arquivo possui o tema customizado e estilos com tamanhos e cores de
textos, de maneira simular a um arquivo .css de pginas web.
Nesse tema customizado foi configurado a propriedade android :windowNoTitle
para true, para que o ttulo da barra de status da aplicao seja omitido, com o
intuito de ganharmos espao na tela.
A seguir podemos ver o arquivo que contm as configuraes de cores desse
projeto. O arquivo tambm tem cores que no vamos utilizar, mas que foram
inseridas apenas para exemplificar.
Cap tulo 2 Apli cati vo de exemplo
29
) /res/values/cores.xml
< ? x ml version=''1.0" encoding= utf-8?>
<resources>
<color name= " fundo" > # ffffff< /color>
< color name= " amarelo> # F F A500< /color>
< color name= " az ul" > # 0000ff< /color>
< color name= " branco" > # ffffff< /color>
< color name= " cinz a_ c" > # cccccc< /color>
< color name= " cinz a_ d" > # dddddd< /color>
< color name= " cinz a_ e" > # eeeeee< /color>
< color name= " laranj a" > # F F A500< /color>
< color name= "preto" > # 000000< /color>
< color name= " transparente" > # 00000000< /color>
< color name= " verde" > # 00ff00< /color>
< color name= " vermelho" > # ff0000< /color>
</nesources>
Se voc preferir, pode importar no worspace o projeto LivroAndroid-
Template como base para acompanhar os exemplos. Esse projeto
tem a estrutura de pastas j criadas e contm todas as imagens que
vamos utilizar.
2.3 Criando a tela principal - Dashboard
No Google I /O 2011 foram apresentadas diversas palestras sobre Android, e o
Google criou um aplicativo demonstrando algumas boas prticas no desenvol
vimento de aplicaes mobile para Android.
Essa aplicao se chama iosched e pode ser encontrada no site http://code.google.
com/p/iosched/.
Uma das funcionalidades que essa aplicao demonstra o padro dashboard
para a tela inicial, onde temos o menu da aplicao. Felizmente o Google liberou
o cdigo-fonte da classe DashboardLay out.j ava que pde ser encontrada no material
de download do livro.
Essa classe um gerenciador de layout customizado que vai organizar as
views - neste caso, botes - da tela principal em linhas e colunas, para obtermos
o visual da figura 2.1.
Depois de copiar essa classe para o projeto, inserindo-a no pacote com.google.
android.apps.iosched.ui.widget, necessrio customizar o arquivo /res/layout/main.xml
J0 Googte An droi d para T ablets
i
para utilizar o DashboardLay out. Felizmente esse layout mgico e vai fazer todo o
trabalho pesado de separar as views em duas colunas.
: /res/layout/mai n .xml
<?xml version= " 1.0" encoding=''utf-8"?>
(LinearLayout x mlns:android= " http://schemas.android.com/apk /res/ardroid"
android: orientation^ vertical"
android: layout_width="fill_ parent"
android: layout_height="fill_parent"
android:gravity = " center
android:back ground= " @ color/fundo" >
<include lay out= " @ lay out/include_ header" />
< com.google.android.apps.iosched.ui.widget.DashboardLay out
android:lay out_ width= fill_parent
android:lay out_ height= " wrap_ content"
android :lay out_ weight= T' >
<8utton
android:id= " g+ id/btClassicos
android:tex t= " @ string/menu_ classicos"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:drawableTop= @ drawable/btn_ carro_ classico"
android:drawablePadding= " 10dp"
android:back ground= gnuH "
android:tex tSty le= " bold"
android :textColor=''@color/preto" />
<Button
android:id= @+id/btLux o"
android:tex t= " gstring/menu_ lux o"
android: lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content
android:drawableTop= " gdrawable/btn_ carro_ lux o"
android:drawablePadding= " 10dp"
android: background=''@null"
android:tex tSty le= " bold"
android:tex tColor= " @ color/preto"
/>
<Button
android:id=" tid/btEsportivos
android:tex t= " @ string/menu_ esportivos"
android:lay out_ width= " wrap_ content"
android :lay out_ height= " wrap_ content
android:drawableTop= " @ drawable/btn_ carro_ esportivo"
3
Cap tulo 2 Apli cati vo de exemplo
31
android:drawablePadding= " 10dp"
android: back ground= " @ null"
android:tex tSty le= " bold"
android: tex tColor= " @ color/preto"
/>
< Button
android: id^ g+ id/btSobre
android:tex t= " @ string/menu_ sobre"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android: drawableTop= " @ drawable/btn_ sobre"
android: drawablePadding= ' ' 10dp
android:back ground= " @ null"
android:tex tSty le= " bold
android:tex tColor= " @ color/preto"
/>
< /com.google.android.apps.iosched.ui.widget.DashboardLay out)
n d u d e lay out= " @ lay out/include_ footer" />
< /LinearLay out>
O arquivo /res/layout/main.xml representa o menu principal com o dashboard e
possui os quatro botes da aplicao. Note que quando criamos a tag do Dashboar
dLay out utilizamos o nome completo da classe, pois ela uma view customizada.
< com. google. android. apps. iosched. ui .widget. DashboardLay out)
Nesse layout estamos incluindo outros dois arquivos para criar o cabealho e
o rodap da tela, que so o /res/layout/indudejieader.xml e o /resllayoat/indudejoo-
ter.xml, respectivamente, os quais vamos criar na seqncia da explicao. Neste
momento o projeto ainda no vai compilar, pois ainda vamos criar estes arquivos
de includes.
Tambm estamos internacionalizando os textos para organizar o cdigo, como,
por exemplo, a chave android:tex t= " @ string/menu_ esportivos" . Portanto, crie o arquivo /
res/values/strings.xml da seguinte maneira:
(Alguns dos textos presentes nesse arquivo sero referenciados posteriormente.)
/res/values/stri n gs.xml
< ? xml version= " 1.0 encoding= " utf-8" ? >
< resources>
< string name=" app_ nafne" > Car )s< /string>
<string name= " aguarde" > Por favor aguarde...< /string>
< string name= " copy right" > LivroAndroid - Todos os direitos reservados< /string>
32 Google An droi d para T ablets
< string name=''titulo>Carros 5howcase< /string>
<!-- M ensagens -->
< string name= " erro_ conex ao_ indisponivel" >
Conex o indisponvel, por favor ative sua rede mvel ou Wi-Fi.
< /string>
< string name=" erro_ io" > O oops, sistema indisponvel nom momento.< /string>
<!-- Labels -->
<string name='' abrirSite">Abrir o Site< /string>
<string name= " menu_ lux o>Luxo< /string>
<string name=" menu_esportivos' ' > Esportivos< /string>
<string name= " menu_ classicos" > Clssicos< /string>
<string name= " menu_ sobre" > Sobre< /string>
<string name= " nome" > Nome< /string>
<string name= " descricao" > Descrio< /string>
< /resources>
Neste layout que criamos, cada boto possui uma imagem associada. Por exem
plo, o boto de carros esportivos possui essas duas imagens, conforme a figura
2.8, que so utilizadas quando o boto est no seu estado normal ou pressionado.
Figura 2.8 - Boto carro esportivo nos estados normal e pressionado.
Os nomes das imagens so btn_carro_esportivo_off.png e btn_carro_esportivo_
on.png, respectivamente e podem ser inseridas nas pastas fres/drawable-mdpi e Iresl
drawable-hdpi.
Geralmente podemos utilizar imagens como o fundo das views com a nota
o @ drawable/nomeJ .magem. Mas neste caso temos duas imagens que desejamos usar
dependendo do estado do boto, normal ou pressionado.
Para isso, no boto usamos uma terceira imagem no formato de XML que
agrupa essas duas e controla o seu estado. Esse recurso chamado no Android
de State Drawables.
Para criar esse arquivo de configurao do estado da imagem precisamos
criar um arquivo XML na pasta /res/drawable, conforme demonstrado a seguir.
Note que vamos utilizar a pasta /res/drawable padro, sem nenhum dos prefixos
de densidade (ldpi, mdpi, hdpi).
Cap tulo 2 Apli cati vo de exemplo
33
& /res/drawable/btn _carro_esporti vo.xml
< ? x ml versior>''1.0" encoding="utf-8'' ?>
< selector x mlns:android= " http://schemas.android.com/apk /res/android">

<!-- Efeito especial de on/off -->


< item android:drawable= " @ drawable/btn_ carro_ esportivo_ off" android:state_ pressed= " false"
android:state_ focused= " false" />
< item android:drawable= " @ drawable/btn_ carro_ esportivo_ off" android:state_ pressed= false"
android:state_ focused= " true" />
< item android:drawable= " @ drawable/btn_ carro_ esportivo_ on" android:State_ pressed= " true"
android:state_ focused= " false" />
< /selector>
O arquivo XML /res/drawable/btn_carro_esportivo.xml agrupa as duas imagens
btn_carro_esportivo_off.png e btn_carro_esportivo_on.png e agora pode ser utilizado
como uma imagem normal no layout da tela. Isso mesmo! O arquivo um XML,
mas o Android transforma esse arquivo em uma imagem.
< Button
android: id= "(8+id/btEsportivos"
android:tex t= " @ string/esportivos"
android: drawableTop= " @ drawable/btn_ carro_ esportivo"
/>
Esse processo de criao de duas imagens para o estado normal e pressionado
trabalhoso, mas infelizmente necessrio para dar um acabamento profissional
a sua aplicap.
Se voc no estutlizando o projeto template, baixe o projeto de
exemplo finalizado para copiar as imagens dos botes e inserir nas
pastas /res/drawable-mdpi e /res/drawable-hdpi apropriadamente.
No captulo 4 vamos estudar como criar aplicaes para vrios
tamanhos de telas e resolues e entender os identificadores de
densidade, como ldpi, mdpi e hdpi. Copie tambm os outros arquivos
i de imagem para os botes dos carros clssicos e de luxo.
Da mesma forma que criamos o efeito para o boto dos carros esportivos, po
demos fazer o mesmo para os carros clssicos e de luxo, conforme demonstrado
a seguir.
34 Google An droi d para T ablets
i /res/drawable/btn _carro_dassi co.xml
<? xml version= " 1.0" encoding= " utf-8?>
< selector x mlns:android= " http://schemas.android.com/apk /res/android">
<!-- Efeito especial de on/off -->
< item android:drawable= " gdrawable/btn_ carro_ classico_ off' android:State_ pressed= " false"
android:state_ focused= " false" />
< item android:drawable= " gdrawable/btn_ carro_ classico_ off" android:state_ pressed= " false"
android:state_ focused= true" />
< item android:drawable= gdrawable/btn_ carro_ classico_ on' ' android:state_ pressed= true"
android:state_ focused= " false" />
</selector>
/res/d ra wa bl e/btn _ca rro J uxo.xm I
< ? xml version= " 1.0" encoding= " utf-8" ? >
< selector x mlns:android= " http://schemas.android.com/apk /res/android">
<!-- Efeito especial de on/off -->
< item android:drawable= " gdrawable/btn_ carro_ lux o_ off android:state_ pressed= " false"
android:state_ focused= " false" />
< item android:drawable= " drawable/btn_ carro_ lux o_ off" android:state_ pressed= " false"
android:state_ focused= " true" />
< item android:drawable= " gdrawable/btn_ carro_ lux o_ on" android:state_ pressed= " true"
android: state_ focused= " false" />
< /selector>
Depois de criar os botes para carros clssicos, de luxo e esportivos, tambm
precisamos criar o boro utilizado para abrir a tela de sobre.
Para isso vamos criar o arquivo /rcs/dmwable/btn_sobre.xml conforme visualizado
a seguir. Note que voc precisa copiar as imagens btn_sobre_off.png e btn_sobre_on.png
que acompanham os exemplos do livro.
/res/drawable/btn _sobre.xml
<? xml version= " 1.0" encoding= " utf-8" ? >
< selector x mlns:android= " http://schemas.android.com/apk /res/android">
<!-- Efeito especial de on/off para o boto -->
< item android:drawable= gdrawable/btn_ sobre_ off" android:state_ pressed= " false"
android:state_ focused= " false" />.
< item android:drawable= drawable/btn_ sobre_ off" android:state_ pressed= " false"
android:state_ focused= " true" />
< item android:drawable= drawable/btn_sobre_on" android:state_ pressed= " true"
android:state_ focused= " false" />
</selector>
Cap tulo 2 Apli cati vo de exemplo 35
2.4 Criando os layouts de cabealho e rodap
Note que nas figuras que exibem as telas da aplicao temos dois layouts que
foram inseridos no cabealho e no rodap da tela, os quais foram includos no
arquivo /res/layout/main.xml utilizando a tag < include> .
Vamos verificar o arquivo main.xml novamente, de forma resumida. Note que os
includes foram feitos antes e depois do DashboardLay out. I mportante notar que nessa
tela o DashboardLay out foi configurado com peso = 1 com o atributo android :lay out_
weight= " l" . Esse atributo faz com que esse layout seja esticado para ocupar a tela
inteira, sendo que ele o nico que possui peso.
/ res/layout/mai n .xml
<? xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android
android: orientation= " verticar
android :layout_width= "fill_parent"
>
dnclude lay out= " @ lay out/include_ header" />
< com.google.android.apps.iosched.ui.widget.DashboardLay out
android: lay out_ width= " fill_parent"
android: layout_height=' 'wrap_content"
android:lay out_ weight= " l" >
<Button
android:id= " @ + id/btLux o"
android:tex t= Luxo"
/>
< /com.google.android.apps.iosched.ui.widget.DashboardLay out)
nclude layout=''|81ayout/include_footer" />
< /LinearLay out>
A tcnica de utilizar pesos para esticar um layout muito utilizada. Neste
layout foi utilizado a tag android:lay out_ weight= " l" para que o Dashboard ocupe a
tela inteira, deixando os layouts do header e footer pequenos.
Os arquivos de cabealho e rodap so simples, e podemos visualiz-los a seguir.
/res/layout/i n dude_header.xml
< ?x ml version= '1.0encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas. android.com/apk /res/android"
36 Google An droi d para T ablets
android:id= "|8+id/layoutHeader"
android:orientation= " vertical
android: layout_width="fill_parent''
android:lay out_ height= " 55dp"
android: gravity = " center"
android:back ground= " @ drawable/shape_ header" >
< Tex tView
android:id=' '@+ id/tHead er"
android:layout_ width= "fillj )arent'
android: lay out_ height= wrap_content:"
android:text= " @ string/titulo"
android:gravity =" center"
style=',gsty le/tex tol4Negrito"
android:tex tColor= " @ color/branco />
< /linearLay out>
( * i /res/layout/i n dude_f ooter.xml
< ? x ml version= 1.0" encoding= utf-8"?>
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android:layout_width=" fill_parent"
android:lay out_ height= " 40dp
android: gravity='' center
android:back ground= " @ drawabIe/shape_ footer >
< Tex tView
android:lay out_width="fill_parent"
android: layout_he Blit= "wrap_content
android:tex t= @ 5tring/copyright"
android:gravity = " center
sty le= " @ sty le/tex tol2"
android:tex tColor= @ color/branco"
/>
< /LinearLay out>
O arquivo de cabealho utiliza o texto @ string/titulo, e o arquivo de rodap
utiliza a chave gstring/copy right, sendo que ambas ns j criamos anteriormente
no arquivo /res/values/strings.xml.
Note que ambos os layouts tambm utilizam uma imagem de fundo, que so
@ drawable/shape_ header e @ drawable/stiape_ footer, respectivamente.
Para criar essas duas imagens temos um problema, pois precisamos criar uma
imagem bonita com um dgrad azul. Mas o problema saber para qual resoluo
de tela devemos criar.
Cap tulo 2 Apli cati vo de exemplo 37
Podemos criar uma imagem para celulares HVGA de 320x480 pixels ou
celulares V/VGA de 480x800 pixels, por exemplo. Para isso existem as pastas /
res/drawable-mdpi e /res/drawable-hdpi, respectivamente. Mas vamos estudar mais
detalhes sobre isso no capmlo 4.
Neste momento, para facilitar a nossa vida, vamos criar uma imagem dina
micamente utilizando o recurso de shapes do Android. Um shape, ou forma se
traduzirmos, pode ser do tipo retngulo, oval ou linha. Neste caso, naturalmente,
vamos criar uma imagem na forma de um retngulo.
A vantagem de se utilizar um shape para isso que podemos criar um efeito
de gradiente para iniciar em uma cor com tonalidade mais forte e terminar em
uma tonalidade mais fraca. Neste caso configuramos o arquivo de cabealho
para comear com um azul opaco e terminar em um azul transparente, e no caso
do arquivo de rodap, o contrrio. No Android a notao de cores no formato
ARGB, em que o primeiro parmetro o alpha, que define a transparncia da cor.
( d /res/drawable/shape_header.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< shape x mlns:android= " http://schemas.android.com/apk /res/android"
android:shape= " rectangle" >
< gradient
android:startColor= " # 000000F F "
android:endColor= " # F F 0000F F "
android:angle= " 90" />
X
</shape>
& /res/drawable/shape_f ooter.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< shape x mlns: android= " http ://schemas. android. com/apk /res/androd"
android:shape= " rectangle" >
< gradient
android: startColor=''#FF0000FF''
android: endColor= " # 000000F F "
android:angle= " 90" />
< /shape>
Ao inserir esses dois arquivos XMLna pasta /res/drawable o Android vai criar dina
micamente uma imagem que representa um retngulo azul e com o efeito gradiente.
Neste momento o projeto vai compilar normalmente e pode ser executado. O resul
tado ser a tela de dashboard, porm os botes ainda no possuem nenhum evento.
38 Google An droi d para T ablets
P
2.5 Adicionando os eventos nos botes do dashboard
Depois de criarmos o layout do dashboard, vamos atualizar a acdvity M ain, que
a principal e o ponto de partida de nossa aplicao.
O que precisamos implementar agora so os eventos dos botes para abrir
as prximas telas. Para isso vamos alterar o cdigo-fonte da classe M ain conforme
demonstrado a seguir.
& Main.java
public dass M ain extends Activity impleaents O nClick Listener {
private Button btEsportivos;
private Button btClassicos;
private Button btLuxo;
private Button btSobrej
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
// Layout do Dashboard
setContentView(R.lay out.main);
btEsportivos = (Button) findViewById(R.id.btEsportivos);
btEsportivos.setO nClick Listener(this);
btClassicos = (Button) findView8y!d(R.id.btClassicos);
btClassicos. setO nClick Listener(tnis);
btLuxo = (Button) findViswById(R.id.btLux o);
btLux o.setO nClick Listener(this);
btSobre = (Button) findViewByld(R.id.btSobre);
btSobre.setO nClick Listener(this);
}
(SOverride
public void onClick (View v) {
Intent intent = new Intent(this, TelaListaCarros.class);
if (v == btEsportivos) {
intent.putExtra(Carro.TIPO , Carro.TIP0_ ESP0RTIV05);
startActivity (intent);
} else if (v == btClassicos) {
intent.putExtra(Carro.TIPO , Carro.TIP0_ CLASSIC0);
startActivity(intent);
} else if (v == btLux o) {
intent.putEx tra(Carro.TIPO, Carro.TIP0_ LU X 0);
startActivity(intent);
} else if (v = btSobre) {
startActivity (new Intent(this, TelaSobre.dass));
}
}
}
^T = o A B &
Cap tulo 2 Apli cati vo de exemplo
2 2 9 6 2 3
Note que ao pressionar os botes para visualizar a lista de carros estamos
utilizando a activity TelaListaCarros, que recebe como parmetro o tipo do carro
selecionado.
i T elaLi staCarros.java
pack age br.livroandroid.carros.activity ;
import android.app.Activity ;
import android.os.Bundle;
import br.livroandroid.carros.R;
public class TelaListaCarros ex tends Activity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
String tipo = getIntent().getStringEx tra(" tipo);
// Buscar os carros e mostrar na tela aqui...
}
}
No se esquea que precisamos definir essa activity no Androidmanifest.xml da
seguinte forma:
< activity android:name= " .activity .TelaListaCarros" />
E st o u utiliz ando o pacote br.livroandroid.carros.activity para as
activiries. Consulte o proj eto d e e x e m p l o se tiver d vidas e q uiser
seguir a m e s m a nomenclatura.
O arquivo de layout da tela que lista os carros pode ser visualizado a seguir.
Por enquanto vamos criar ele bem simples apenas para o projeto compilar.
(Si /res/layout/carros.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:lay out_ width= " match_ parent"
android:lay out_ height= " match_ parent"
android:orientation= " vertical" >
< /LinearLay out>
Para o projeto compilar ainda precisamos criar a classe da activity TelaSobre,
que ser chamada ao clicar no boto sobre no dashboard.
40 Google An droi d para T ablets
i s T elaSobre.java
pack age br.livroandroid.carros.activity j
public d a s s TelaSobre ex tends Activity {
}
Note que agora teremos duas novas activities no AndroidM anifest.x ml, portanto
no esquea de declar-las.
< activity android:name= " .activity .TelaListaCarros" />
< activity android:nawe=".activity .TelaSobre" />
Neste momento, se voc executar a aplicao j ser possvel visualizar a tela
inicial com o dashboard, e os botes j esto respondendo, mas vamos continuar
e melhorar ainda mais o aplicativo.
2.6 A classe Carro
O prximo passo no desenvolvimento do nosso projeto criar a classe Carro, que
ser utilizada como o nosso objeto principal, para armazenar as informaes de
cada carro. Essa classe simples e vai conter apenas o nome, a descrio detalhada,
uma URL coma a foto do carro e outra URL com o site do fabricante.
Para que voc possa visualizar o exemplo desde j, vamos ler um XML de uma
pgina web para criarmos uma lista de carros que ser exibida em um ListView
dentro de nossa aplicao.
A seguir podemos ver o formato desse arquivo XML.
i carros.xml (este arqui vo est n a web)
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< carros>
< carro>
< nome> F errari< /nome>
< desc> F errari desc< /desc>
< url_ info> http://www.livroandroid.com.br/livro/carros/ferrari.htm< /url_ info>
< url_ foto> http://www.livroandroid.com.br/livro/carros/iug/ferrariSaAperta.j pg< /url_ foto>
< /carro>
< /carros>
Como vamos fazer a leitura desse arquivo XML, a nossa classe Carro precisa
possuir exatamente os mesmos atributos que esto nesse arquivo, conforme
demonstrado a seguir.
Cap tulo 2 Apli cati vo de exemplo
41
) Carro.java
pack age br.livr android.carros.domain;
import j ava.io.Serializ able;
public d a s s Carro implements Serializ able {
private static final long serialVersionU ID = 6601006766832473959L;
public static final String K EY = "carro";
public static final String TIPO = "tipo"j
public static final String TIP0_ CLASSIC0 = "clssicos";
public static final String TIPO _ ESPO RTIVO S = " esportivos";
public static final String TIPO _ LU X O = "luxo";
public String nome;
public String desc;
public String urlF oto;
public String urllnfo;
} .
Note que a classe implementa a interface Serializ able para permitir a serializao
deste objeto, a fim de facilitar a passagem de parmetros entre as telas. Fizemos isso
porque no momento em que um carro for selecionado na tela principal, vamos
passar o objeto selecionado para a prxima tela, para recuperar as informaes e
visualizar os detalhes do carro desejado.
Note que definimos trs constantes na classe (poderiam ser enums) para diferen
ciar o tipo do carro selecionado no dashboard, que podem ser carros clssicos,
luxo ou esportivos.
2.7 Arquitetura de integrao com o legado
Em projetos reais voc ter que integrar sua aplicao com algum legado, onde
existe um banco de dados com as informaes que precisamos disponibilizar no
aplicativo.
Uma dvida comum de desenvolvedores de aplicativos mobile a seguinte: qual
a maneira correta de conectar e buscar informaes no banco de dados do cliente?
Na prtica isso no acontece, e o celular no vai conectar diretamente no
banco de dados, seja por motivos de complexidade ou at mesmo segurana das
informaes.
Dessa forma a maneira recomendada de integrao criar alguma pgina ou
servio que ser publicado no servidor. Essa pgina pode ser um servlet, ASP ou
PHP, qualquer coisa que retorna um arquivo XML ou J SON. Tente evitar web
services (WSDL) tradicionais, pois a integrao mais trabalhosa.
Neste livro vamos optar pelo servidor retornar um simples arquivo XML, o que
facilitar o entendimento do exemplo. Lembre-se que a vantagem de sua pgina
web retornar um arquivo XML ou J SON que possvel ler essas informaes
no somente em um smartphone com Android, mas tambm em qualquer outro
sistema operacional, seja a aplicao mobile ou no.
Como implementar o servidor est fora do escopo do livro, para facilitar vamos
utilizar um simples arquivo XML que est publicado no site. _
Para isso teremos trs arquivos, um para cada tipo de carro.
http://www.livroandroid.com.br/livro/carros/carros_ esportivos.xml
http: //www. livroandroid. com. br/livro/carros/carros j rlassicos. xml
http://www.livroandroid.com.br/livro/carros/carros_ lux o.xml
Entenda que na prtica teramos uma pgina web dinmica que retomaria
um XML como esse. Possivelmente, em uma aplicao real teramos um site com
a administrao do sistema com o cadastro dos carros. Mas para este exemplo
vamos ler direto esse arquivo XML.
2.8 Requisio HTTP para ler o arquivo XML
O prximo passo fazermos uma requisio HTTP no servidor web para buscar
a lista de carros.
Primeiramente vamos criar a estrutura utilizando o link dos carros esportivos
e depois adaptamos para buscar qualquer tipo de carro.
http:/ / www.lwroandroid.co1n.br/ Uvro/ carros/ carros_csportivos.x17d
Para efetuar a requisio HTTP no servidor web vamos criar a classe H ttpH elper
conforme demonstrada a seguir.
Essa classe bem simples. Ela faz uma requisio do tipo GET no servidor web
e converte o retorno para uma string, a qual representa o arquivo XML.
HttpHelper.java
pack age br.livroandroid.utils;
public class H ttpH elper {
private static final String TAG = "Http";
public static boolean L0G_0N = false;
public static String doGet(String uri, String charset) throws IO Ex ception {
if (L0G_ 0N) {
Log.d(TAG, " H ttp.doGet: " + uri);
}
42 Google An droi d para T ablets
Cap tulo 2 Apli cati vo de exemplo
43
U RL u = new U RL(url);
H ttpU RLConnection conn = (H ttpU RLConnection) u.openConnectionQ ;
conn.setReq uestM ethod(GET" );
conn.setDoO utput(true);
conn.setDoInput(true);
conn.connect();
InputStream in = conn.getInputStream();
String s = IO U tils.toString(in, charset);
if (L0G_0N) {
Log.d(TAG, "<< H ttp.doGet: " + s);
}
in.closeQ ;
conn.disconnect();
return s;
}
}
Noie que o mtodo doGet(url, charset) recebe o charset do contedo que o ser
vidor vai retornar, que muitas vezes varia entre IS0-8859-1 e U TF -8. Para que o texto
seja lido corretamente com o charset informado, vamos criar uma classe IO U tils.
S d lOUti ls.java
pack age br.livroandroid.utils;
public class IO U tils {
private static final String TAG = "IOUtils";
public static String toString(InputStream in, String charset) throws IO Ex ception {
by te[] by tes = toBy tes(in);
String tex to = new String(by tes, charset);
return texto;
}
public static byte[ ] toBy tes(InputStream in) {
By teArray O utputStream bos = new By teArray O utputStreamQ ;
try {
byte[] buffer = new by te[ 1024] ;
int len;
while ((len = in.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
by te[] by tes = bos.toBy teArray ();
return bytes;
} catch (Ex ception e) {
Log.e(TAG, e.getH essage(), e);
return null;
} finally {
44 Google An droi d paraT ablets
try {
bos.closeQ ;
in.close();
} catch (IO Exception e) {
Log.e(TAG, e.getM essageQ , e);
}
}
}
}
Utilizar a classe H ttpH elper simples. Basta uma linha de cdigo informando a
URL do servidor e o charset de retorno do arquivo.
String xml = HttpH elper.doGet(" http://www.livroandroid.coin.br/livro/carros/carros_esportivos.xml",
"UTF-8");
S que como temos trs tipos de carros que podem ser selecionados, vamos uti
lizar uma URL genrica para que funcione independente do tipo do carro desejado.
http://www.livroandroid.com.br/livro/carros/carros_ltipo]. xml
Antes de fazer a requisio, o parmetro {tipo} na URL ser substitudo pelo tipo
desejado. O tipo informado ser uma das trs constantes definidas na classe Carro.
public static final String TIP0_ CLASSIC0 = "clssicos";
public static final String TIP0_ ESP0RTIV0S = "esportivos" ;
public static final String TIP0_ LU X 0 = "luxo";
Depois de ter feito a requisio na web e recuperado o XML com os carros,
precisamos fazer o parser do XML e criar uma lista de carros a partir dele. Para
isso vamos criar outra classe que vai centralizar esse tipo de acesso, simplificando
o cdigo e facilitando a manuteno do projeto.
d) CarroServi ce.java
pack age br.livroandroid.carros.Service;
public class CarroService {
private static final String U RL = "http://www.livroandroid.coni.br/livro/carros/carrosJ tipo} .x ml";
public static List<Carro> getCarros(Contex t contex t, String tipo) throws IO Ex ception {
String uri = U RL.replace(" { tipo} , tipo);_
String xml = HttpH elper.doGet(url, " U TF -8);
List< Carro> carros = parserX M L(contex t, xml);
return carros;
}
private static List<Carro> parserX M L(Contex t context, String xml) {
// F azer o parser do XM L e retornar a lista de carros aq ui
return new Array List< Carro> ();
}>
Cap tulo 2 Apli cati vo de exemplo
45
Note que a classe CarroService faz apenas a requisio no servidor para buscar o
XML e chama o mtodo parserX M i^ contex t, x ml) para fazer o parser do XML e criar
a lista de carros.
Agora precisamos implementar o parser do XML. Para isso vamos criar outra
classe especializada nesse assunto. Felizmente, as APIs de parser de XML dis
ponveis no J ava esto todas nossa disposio no Android, como as famosas
APIs de SAX e DOM. Tambm podemos inserir como uma biblioteca no projeto
populares frameworks de parser XML, como o J DOM ou o XStream, e utiliz-lo
sem problemas.
Mas nesse projeto vamos criar nossa prpria classe de parser XML bem simples
que utiliza a API DOM, do pacote org.w3c.dom. Essa classe pode ser visualizada a seguir.
XMLUti ls.java
pack age br.livroandroid.utils;
import j avax .x ml.parsers.DocumentBuilder;
import j avax .x ml.parsers.DocumentBuilderF actory ;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class X M LU tils {
// Retorna a tag/n raiz do XM L
public static Element getRoot(String xml, String charset) {
try {
InputStream in = null;
DocumentBuilderF actory factory = DocumentBuilderF actory .newlnstance();
factory .setValidating(false);
byte[] by tes = xml.getBytes(charset);
in = new By teArray lnputStream(by tes);
DocumentBuilder builder = factory .newDocumentBuilderQ ;
Document dom = builder.parse(in);
Element root = dom.getDocumentElement();
if (root = = null) {
throw new RuntimeEx ception(" X M L invalido" );
}
return root;
} catch (Ex ception e) {
throw new RuntimeEx ception(e.getH essage(), e);
}
// Retorna o tex to dentro da tag
public static String getTex t(Node node, String tag) {
Node n = getChild(node, tag);
if (n != null) {
Node tex t = n.getFirstChild();
if (text != null) {
String s = text.getNodeValue();
return s.trim();
}
}
return
}
// Retorna as tags filhas do node informado
public static List<Node> getChildren(Node node, String name) {
List< Node> children = new Array List< Node> ();
NodeList nodes = node.getChildNodes();
if (nodes != null 8& nodes.getLengthQ >= 1) {
for (int i = 0; i < nodes.getLengthQ ; i++) {
Node n = nodes.item(i);
if (name.eq uals(n.getNodeName())) {
children.add(n);
}
}
}
return children;
}
public static Node getChild(Node node, String tag) {
if (node == null) {
return null;
}
NodeList childNodes = node.getChildNodes();
if (childNodes == null) {
return null;
}
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (item != null) {
String name = item.getNodeName();
if (tag.eq ualsIgnoreCase(name)) {
return item;
}
}
}
return null;
Google An droi d para T ablets
Cap tulo 2 Apli cati vo de exemplo 47
&
Na tabela 2.1 podemos verificar a explicao de cada mtodo da classe XMLUtils.
Tabela 2.1 - Mtodos da classe X M LU tils
M todo Descrio
getRoot(x ml,charset)
Inicia o parser do XML e retorna a tag raiz deste. Utilizado no
exemplo para recuperar a primeira tag <carros>.
getTex t(node,charset)
Retorna o texto de uma tag, possibilitando a leitura do
contedo das tags <nome>e <desc>, por exemplo.
getChildren(node, name)
Retorna as tags filhas da tag informada. Por exemplo, a tag
<carros>contm diversas tags filhas <carro>. Esse mtodo
utilizado para retornar a lista com todos os carros do XML.
getChild(node, tag)
Retorna uma tag filha da tag informada. Digamos que dentro
da tag < carro> seja necessrio ler a tag <nome>. Esse mtodo
utilizado para isso.
Depois que criamos uma classe especializada em parser XML, podemos com
pletar a classe CarroService para fazer o parser da maneira correta.
& CarroServi ce.java
pack age br.livroandroid.carros.Service;
public class CarroService {
private static final String URL = "http://www.livroandroid.com.br/livro/carros/carros_ { tipo} .x ml";
private static final boolean LO G_O N = false;
private static final String TAG = "CarroService";
public static List< Carro> getCarros(Contex t context, String tipo) throws IO Ex ception {
String uri = U RL.replace({tipo} ", tipo);
String xml = H ttpH elper.doGet(url, "UTF-8'');
List< Carro> carros = parserX M L(contex t, xml);
return carros;
}
private static List< Carro> parserX M l(Contex t contex t, String xml) {
List< Carro> carros = new Array List< Carro> ();
Element root = X M LU tils.getRoot(x ml, "UTF-8");
// L todas as tags <carro>
List< Node> nodeCarros = X M LU tils.getChildren(root, "carro");
// Insere cada carro na lista
for (Node node : nodeCarros) {
Carro c = new CarroQ ;
// L as informaes de cada carro
c.nome = X M LU tils.getTex t(node, "nome");
c.desc = X M LU tils.getTex t(node, "desc");
* c.urlF oto = X M LU tils.getTex t(node, "url_ foto");
c.urllnfo = X M LU tils.getTex t(node, "url_info");
if (L0G_0N) {
Log.d(TAG, "Carro " + c.nome + " > " + c.urlFoto);
48
Google An droi d para T ablets
}
carros.add(c);
}
if (LO GO N) {
Log.d(TAG, carros.siz eQ + " encontrados." );
}
return carros;
}
}
Com a ajuda da classe X M LU tils que criamos anteriormente foi possvel fazer o
parser do XML e transform-lo facilmente em uma lista de objetos Carro. O pr
ximo passo chamarmos esse mtodo dentro de nossa activity
Anteriormente criamos a classe TelaListaCarros, que vai utilizar o arquivo de
layout / res/ layout/ carros.xml, mas o seu contedo inicialmente estava vazio.
Agora podemos alterar essa classe para fazer a busca dos carros na web, onde
o parser do XML realizado e uma lista de objetos retornada. Para comear a
brincadeira vamos apenas imprimir os nomes de cada carro, sem exibir nada na
tela por enquanto.
T ela Li staCarros.java
pack age br.livroandroid.carros.activity ;
public class TelaListaCarros ex tends Activity {
private static final String TAG = " livroandroid";
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView( R.lay out.carros);
try {
String tipo = getIntent().getStringEx tra(" tipo);
List< Carro> carros = CarroService.getCarros(this, tipo);
for (Carro c : carros) {
Log.i(TAG, "Carro: " + c.nome);
}
} catch (IO Ex ception e) {
Log.e(TAG, e.getM essageQ , e);
}
}
}
Essa classe utiliza o arquivo /res/layout/carros.xml, e voc pode criar esse arquivo
com qualquer layout neste momento, pois vamos customiz-lo posteriormente.
Cap tulo 2 Apli cati vo de exemplo
49
Agora vamos executar o exemplo e buscar os carros na internet. Lembre-se que
para isso seu celular deve possuir um plano com conexo de dados ou estar co
nectado via Wi-Fi* ou, claro, utilizar o emulador.
Ao executar esse exemplo podemos visualizar no logCat as seguintes informa
es, comprovando que a busca e o parser do XML foram realizadas com sucesso.
Lembre-se que as informaes sobre os carros podem ser alteradas no servidor,
pois estarei esporadicamente atualizando a lista.
INF O /carros: Carro: F errari
INF O /carros: Carro: Porsche
INF O /carros: Carro: Lamborghini
INF O /carros: Carro: Corvette
INF O /carros: Carro: BMW
Um problema grave com a implementao desse exemplo que
ele no est utilizando threads. Nesse caso interface de tela vai
travar at que a busca seja retornada e talvez o famoso erro ANR
(Application Not Respondmg) possa acontecer. Mas no se preocupe,
pois nos prximos tpicos vamos melhorar a implementao para
a correta utilizao de threads. Lembre-se tambm que ainda no
criamos o arquivo /resayaut/carros.xml para a lista de carros, mas
faremos isso em breve.
2.9 Criando um projeto biblioteca para o projeto
Se voc seguiu esse exemplo passo a passo, deve ter conseguido imprimir os nomes
dos carros no LogCat. Note que ainda no fizemos nada visualmente.
Mas antes de nos preocuparmos em avanar no aplicativo, est na hora de
comearmos a organizar os padres utilizados. E recomendado fazer isso logo
no incio, correto?
A primeira tarefa a se fazer extrair classes utilitrias, como a HttpHelper,XMLUtils
e IOUtils, que so um tipo de biblioteca, para outro projeto. O motivo disso bvio:
essasxlasses de infraestrutura sero utilizadas por muitos projetos posteriormente.
Portanto, vamos criar um projeto Android do tipo biblioteca com os seguintes
dados:
Nome do Projeto: LivroAndroid-AndroidU tils
Verso: A ndroid 23.x
Pacote: br.livroandroid.utils
50
Google An droi d para T ablets

No precisa criar nenhuma activity.


Verso mnima do SDK: 4 (Android 1.6)
Note que no existe nada de mais ao criar este projeto. A nica diferena que
no criamos nenhuma activity, uma vez que o objetivo no executar este projeto,
e sim utiliz-lo como uma biblioteca para outros.
Agora vamos mover as classes H ttpH elper, X M LU tils e ioutils criadas no projeto
dos carros para este projeto que ser nossa biblioteca com classes utilitrias. Para
isso vamos criar o pacote br.livroandroid.utils neste projeto.
Como resultado devem existir no workspace os dois projetos, onde as classes
utilitrias foram corretamente movidas para o projeto biblioteca, conforme de
monstrado na figura 2.9.
i Hierarchy^
B~^> vroAndroid-AndroidUtils
I B - & src
B - f f i br.livroandroid.utils
~2| HttpHelper.java
" [ Z ) IOUtils.java
0- [ J ] XMLUtils.java
$ 3 gen [Generated J ava Files]
Android 2.3.3
assets
eS' res
^] AndroidManifest.xml
|=] default.properties
y?| proguard.cfg
LivroAndroid-Cap02-Carros
-
j|jB br.livroandroid.carros
0 - 0 Main.java
fi br,livroandroid.carros.domain
- [ J ) Carro.java
jB br.livroandroid.carros.service
b-S EESB5B53E B
gen [Generated J ava Files]
Android 2.3.3
assets
res
AndroidManifest.xml
default.properties
proguard.cfg
Figura 2.9 - Projeto biblioteca sendo criado.
Note que, como movemos as classes para outro projeto, obviamente agora a
classe CarroService parou de compilar.
Cap tulo 2 Apli cati vo de exemplo
51
A configurao que est faltando adicionar o projeto LivroAndroid-Androidlltils
como dependncia no projeto dos carros.
Mas antes precisamos configurar o projeto LivroAndroid-AndroidU tils para ser
uma biblioteca. Portanto, vamos entrar nas propriedades do projeto, e no item
de menu A ndroid vamos selecionar o checkbox Is Librany , conforme a figura 2.10.
Feito isso o projeto ser transformando em um tipo de biblioteca e no poder
mais ser executado como uma aplicao normal. O prximo passo adicionar
essa biblioteca no projeto dos carros. Para isso entre nas propriedades do projeto
dos carros no mesmo lugar, mas desta vez clique no boto Add para adicionar uma
dependncia, conforme as figuras 2.11 e 2.12.
Note que somente projetos do tipo biblioteca vo aparecer na lista para serem
adicionados como dependncia.
Neste momento o projeto dos carros j possui a dependncia com o projeto
biblioteca que contm as classes utilitrias, e consequentemente o projeto voltar
a compilar.
riroid'
f-Blalders
- J ava BuJ d Pith
0 -J ava Code Styfe
S- J ava Correter
E- iavaEdtor
. J avadoc Loubcn
' Project References
Run/Debug Sttirajs
S-TskReoatory
TaskTags
E-Vadsbon
UddT*it
ndrGc
Projt
TaretNmft v.' Vendc^-^VV-'! 1 Ptotfofw I A P ...I j
P I An iad3, 0 Android Open Soirce Project
Google API s Googfe Inc.
AndWdl. 6 Android Open S0* e Project
An*cd2.2 Android Cpen S a r a Project
O Googe API s Goo^ Inc.
GALAXYTaAd... SamsungEfectrorcsCo.,Ud.
0 Andvid 2.3.3 Android Open Souxe Pro)t
Google API s Google Inc.
2,2 8
Ltd. 2,2 8
2.3.3 10
2.3.3 10
3,0 11
Figiira 2.10 - Projeto biblioteca.
Google An droi d para T ablets
Figura 2.11 - Projeto biblioteca sendo adicionado.
| type ftertext
-Resource
-BuOders
J ava Build Path
El- J ava Code Style
0 - J ava Compilei
Eh J ava Editor
J avadoc Loeation
Project References
Refactoring ttstory
| Run/Debug Settings
B Task Repository
jTask Tags
0- Validation
L WikJ Text
Android Open Source Project
Artdroid Open Source Project
Google Inc.
Samsung Electronics Co., Ltd.
Android Open Source Project
Google Inc.
Artioid Open Soixce Project
Google Inc.
Figura 2.12 - Projeto biblioteca adicionado com sucesso.
Note que desenvolvedores J ava esto acostumados a utilizar a opo Java Bui ld
Path nas propriedades do projeto para adicionar dependncias, sejam projetos ou
arquivos .jar. Arquivos .jar que possuem as classes de algum framework continuam
sendo utilizados da mesma forma, mas para adicionar um projeto como dependn
cia recomendada a utilizao de um projeto nativo, como biblioteca do Android,
como demonstramos aqui. A vantagem de se utilizar um projeto biblioteca que
no somente as classes so compartilhadas, mas tambm todos os recursos do
projeto criados nas pastas /res/valuesjres/layouts e./res/drawable. E sabemos que para
gerenciar esses recursos criada uma classe R no projeto biblioteca.
Sendo assim, como o pacote do projeto LivroAndroid-AndroidU tils br.livroandroid.
utils, sabemos que foi criada a classe br.livroandroid.utils.R.
Portanto, no projeto carros podemos acessar as classes da biblioteca e muito
mais. E, claro, podemos utilizar a classe br.livroandroid.utils.R, que disponibiliza
o acesso a todos os recursos deste projeto!
Faa o teste e veja que se voc criar imagens, arquivos de layout, textos, ani
maes, temas e muito mais no projeto biblioteca, tudo agora estar acessvel no
projeto dos carros, ou em outro, utilizando a classe br.livroandroid.utils.R.
Um projeto biblioteca no pode ser executado sozinho. Ele precisa
ser utilizado como dependncia em outro projeto.
Cap tulo 2 Apli cati vo de exemplo 53
2.10 Criando testes unitrios com J Unit
No desenvolvimento de aplicativos mobile os testes unitrios continuam tendo
uma grande importncia, e no SDK do Android temos disponvel um framework
de testes unitrios que vamos utilizar agora.
Se voc j utilizou testes unitrios em J ava, deve conhecer o popular framework
J Unit. o mesmo que vamos utilizar agora para testar nossas classes e regras de
negcio da aplicao dos carros.
Mas por que testar?
Para responder a essa pergunta pense no seguinte: Como que voc pode
garantir que a requisio HTTP no servidor web est lendo e interpretando cor
retamente aquele arquivo XML? Como voc pode garantir que a lista de carros
ser corretamente criada?
54 Google An droi d para T ablets
o
Uma forma de testar isso executar a aplicao e visualizar os logs como
fizemos. Ou, claro, posteriormente abrir a tela com a lista de carros e verificar
se estes aparecem l. Mas executar a aplicao a todo instante para validar algo
to simples pode se tornar um processo repetitivo e-desgastante, e tudo pode ser
feito com um simples clique diretamente do Eclipse.
Portanto, vamos criar um projeto para testes do tipo AndroidTestProject, confor-
-me os dados a seguir. Note que existe um campo para informar qual o projeto que
gostaramos de testar, que nesse caso o projeto dos carros criado anteriormente.
Nome d o proj eto: LivroAndroid-Cap02-Carros-TestCase
Projeto para testar: selecione o LivroAndroid-Cap02-Carros
Verso: Android 23.x
Pacote: br,livroandroid.carros.test
Verso mnima do SDK: 4 (Android 16)
A figura 2.13 mostra exatamente como esse projeto deve ser criado.
New Android Test Project
Oe* M 9new Android Test Protact resource
Teftoj stNms; | UvroAfidtiid<ep02-Om^Testc^
'Cutai ' : '' '1
, ... .
f * Use cfefaufc locabon
i nrKrwJ 10:/wcrV 3pace^o^o^o/UvroA ndrcd<dp02<*T OS-T et) | i
. _.....
~T- -j
;;7 v?7'r <vj
Tnettiama -' .'/T vwdo ~l Platfrm- p .:v I - j
0 Android J .6 Android Open 5cut Project 1.6
4 In
Uad2.2 finod Open Sasce Project 2.2
8 1
GoofaAPI s Google Inc. 2.2
0 i
StAXY Tab Addon Sarnsing Dectronc Co., Ltd. 2.2
6 1
Q GoofcAPIs Google Inc.
Q tr3,0 Arefroid Open Sorte Project
Goorjfe API s Gootfe Inc.
Figura 213 - Projeto de testes para os carros sendo criado.
Cap tulo 2 Apli cati vo de exemplo
55
O projeto de testes no possui nenhuma activity e no ser executado como
uma aplicao normal. Quando formos executar um teste no projeto, basta sele
cionar a classe de teste desejada e utilizar o menu Run As > An droi d JUn i t T est.
A primeira e nica configurao importante no projeto est no AndroidManifest.
xml. Vamos examin-lo.
<? xml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.com/apk /res/android"
pack age= " br.livroandroid.carros.test" android:versionCode= " l
android:versionName= l .0>
< uses-sdk android:minSdk Version= " 4" />
< instrumentation android:targetPack age= " br.livroandroid.carros"
android:name= " android.test.InstrumentationTestRunner" />
< application android:icon= " @ drawable/icon" android:label= "@string/app_narae">
< uses-library android:name= " android.test.runner" />
< /application>
< /manifest>
Note que existem duas novas tags no arquivo. A primeira a tag nstrumenta-
tion> , que habilita a utilizao de testes no projeto, e a segunda a incluso do
pacote android.test.runner utilizando a tag < uses-library > .
No necessariamente obrigatrio criar um novo projeto de testes para utilizar
os testes unitrios. Voc pode simplesmente inserir essas duas configuraes no
projeto dos carros, que ele tambm estar apto a executar os testes.
Nosso projeto de testes j est criado, e agora podemos criar o nosso primeiro
teste.
Como candidata a ser testada vamos escolher a classe CarroService e verificar
se ela est fazendo corretamente a requisio no servidor web para buscar a lista
de carros e o parser do XML.
Para isso crie uma classe CarrosTestCase no projeto de testes conforme demons
trado a seguir.
s ) An droi dMan i f est.xml
Para ativar o suporte a testes unitrios no projeto necessrio
declarar a tag < instrumentation> no AndroidManifest.xml para o pacote
da aplicao que ser testado, que nesse caso br.livroandroid.carros.
Feito isso, necessrio importar a biblioteca android.test.runner para
que as classes de testes sejam encontradas no projeto.
56 Google An droi d para T ablets
(Si CarrosT estCase.java
pack age br.livroandroid.carros.test;
public d a s s CarrosTestCase extends AndroidTestCase {
public void testCarrosQ throws IO Ex ception {
Contex t contex t = getContex tQ ;
List< Carro> carros = CarroService.getCarros(contex t,Carro.TIP0_ ESPO RTIVO S);
assertNotNull(carros);
assertTrue(carros.siz e() > 0);
Carro d = carros.get(0);
assertEq uals(" F errari FF", d.nome);
}
}
O objetivo no explicar o J Unit. Como ele um frameworkjava extremamente
popular, vamos apenas utiliz-lo.
Note que a classe de teste deve ser filha de android.test.AndroidTestCase para que
o ambiente de execuo e todos os objetos mocks necessrios para a execuo do
Android sejam corretamente inicializados e disponibilizados. A vantagem de fazer
isso que agora podemos acessar classes como a android.content.Contex t, s quais
geralmente s temos acesso dentro de uma activity. Isso pode ser feito chamando
o mtodo getContex t().
Para executar o teste voc pode selecionar a classe CarrosTestCase e utilizar o
menu Run As >An droi d JUn i t T est. No J Unit, todos os mtodos que comeam com o
nome test sero executados.
No teste estamos fazendo a busca e verificando se a lista de carros retornada
vlida. A inda estamos validando se o nome do primeiro carro Ferrari, pois de
conhecimento do projeto que a Ferrari sempre ser o primeiro carro da lista. Caso
essa informao mude posteriormente, ser necessrio alterar o projeto de teste.
A figura 2.14 exibe o resultado do teste, com a agradvel telinha verde do J Unit
dando-lhe os parabns pela execuo bem-sucedida do teste.
~ I - n p ...........
j~~E testCarros (3,017 s)
i testAnckoldTestCseSetupProperfy (0,029 s)
bi.livtondioid.carros.teslr.CanosTestCase [Rumer: J Unit3](3,046s)
Figura 2.14 - Teste unitrio sendo executado.
Cap tulo 2 Apli cati vo de exemplo
57
Note que poderamos estar pensando em validar vrias regras do sistema, mas
escolhemos um caso bem simples, para simplificar o exemplo.
Mas mesmo neste caso simples podemos verificar os grandes benefcios que
alcanamos com esse teste, que so:
garantimos que a requisio HTTP foi corretamente efetuada;
garantimos que o parser do XML foi corretamente efetuado;
garantimos que a criao da lista de objetos Carro foi realizada com sucesso;
validamos que o primeiro objeto da lista era o carro Ferrari, como o esperado.
Tudo isso sem a necessidade de abrir a aplicao e verificar os carros visual
mente. Fizemos isso com um simples clique diretamente do Eclipse.
Esse tipo de tcnica bastante popular e pode aumentar, e muito, a produtivi
dade do desenvolvimento, uma vez que validaes como essa podem ser realizadas
sem a necessidade de que a aplicao seja executada.
Agora imagine que seu sistema seja um pouco maior e que voc utiliza uma
conexo HTTP com dados de retorno em XML. De repente, por algum motivo
o seu cliente habilita a conexo HTTPS e altera todos os formatos dos arquivos
XML, mudando os nomes das tags, por exemplo. Nesse caso, depois de imple
mentar os novos requisitos, o normal seria abrir tela por tela da aplicao para
validar se tudo continua funcionando depois dessa grande mudana.
Mas voc> um desenvolvedor esperto, preocupado com a qualidade de seu
projeto, e claro que est utilizando testes unitrios. Ento depois de efetuar esse
grande refactor no seu aplicativo, basta executar os testes do projeto.
Isso pode ser feito inclusive todos de uma nica vez, e se voc foi bem-sucedido
na alterao do cdigo, visualizar uma linda e gratificante barra verde, o que vai
deix-lo seguro, tranqilo e feliz da vida com a alterao. Seu servio por hoje esta
r terminado, pode ir para casa descansar, pois voc tem certeza de que tudo est
Ok.Tudo bem, eu sei que mesmo assim voc vai l abrir a aplicao para ter certeza
mesmo. Afinal, eu tambm fao isso. Agente s acredita no que os olhos enxergam,
no ? E, claro, os testes funcionais e de estresse continuam sendo necessrios.
Existem vrias outras tcnicas de testes no Android e muitas outras possibi
lidades. O objetivo aqui foi explicar uma simples e vantajosa abordagem. Para
maiores detalhes extremamente recomendada a leitura da documentao oficial.
58
Google An droi d para T ablets
Lembre-se que o suporte de testes pode ser adicionado a qualquer
projeto, inclusive a projetos do tipo bibliotecas. Portanto, o
recomendado que os projetos biblioteca como o que criamos
anteriormente tambm sejam testados de alguma forma, para que
voc possa garantir que sua infraestrutura e suas principais classes
utilitrias esto funcionando perfeitamente.
2.11 Verificando se existe conexo de dados disponvel
Um cuidado que sempre devemos ter o de garantir que nossa aplicao somen
te realizar alguma requisio web se existir alguma conexo ativa no aparelho,
como, por exemplo, Wi-Fi ou a conexo de dados da operadora.
Portanto, vamos criar uma simples classe chamada Androidutils no projeto bi
blioteca e inserir um mtodo que far essa verificao.
iSd An droi dUti ls.java
pack age br.livroandroid.utils;
public class Androidutils {
protected static final String TAG = " livroandroid";
public static boolean isNetwork Available(Contex t contex t) {
Connectivity M anager connectivity =
(Connectivity M anager) contex t.getSy stemService(Contex t.CO NNECTIVITY _ SERVICE);
if (connectivity == null) {
return false;
} else {
Network Info[ ] info = connectivity .getAllNetwork Info();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[ i] ,getState() == Network lnfo.State.CO NNECTED) {
return true;
}
}
}
}
return false;
}
public static void alertDialog(final Contex t context,final int mensagem) {
try {
AlertDialog dialog = new AlertDialog.Builder(contex t)
. setT itle(contex t. getString(R. string. app_ name))
.setM essage(mensagem)
.createQ ;
Captulo 2 Aplicativo de exemplo 59
I
dialog.setButton(" O K " , new Dialoglnterface.O nClick ListenerQ {
public void onClick (DialogInterface dialogj int which) {
return;
}});
dialog.showQ ;
} catch (Ex ception e) {
Log.e(TAG,e.getM essage(),e);
}
}
}
Para ter acesso ao estado na rede mvel necessrio solicitar a permisso AC-
CESS_ NETW O RK _ STATE. Portanto, vamos adicion-la ao AndroidManifest.xml, totalizando
at o momento duas permisses:
< uses-permission android: name= " android. permission. INTERNET" />
< uses-permission android:name= " android.permission.ACCESS_ NETW O RK _ STATE" />
Agora podemos atualizar o nosso exemplo e verificar se temos uma conexo
disponvel antes de realizar a requisio.
Note que tambm colocamos na classe Androidutils um mtodo chamado
alertDialog(contex tj mensagem) para exibir uma alerta, o qual dispensa comentrios.
Esse alerta recebe a referncia de uma mensagem que vamos inserir no arquivo
res/valus/strings.xml para futura internacionalizao, se necessrio.
#1 /res/values/stri n gs.xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< resources>
< string name= " erro_ conex ao_ indisponivel" >
Conex o indisponvel, por favor ative sua rede mvel ou Wi-Fi.
< /string>
</resources>
Agora que j temos o cdigo que verifica se existe alguma conexo ativa, vamos
atualizar a nossa acvity para buscar os carros normalmente, se existir conexo,
ou exibir um alerta para avisar ao usurio que sua internet est indisponvel.
s T elaLi staCarros.java
public d a s s TelaListaCarros ex tends Activity {
private static final String TAG = "livroandroid" ;
(SOverride
60
Google An droi d para T ablets
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
boolean redeO k = AndroidU tils.isNetwork Available(this);
if (redeOk) {
try {
String tipo = getIntent().getStringEx tra(" tipo);
List< Carro> carros = CarroService.getCarros(this, tipo);
for (Carro c : carros) {
Log.i(TAG, Carro: + c.nome);
}
} catch (IO Exception e) {
Log.e(TAG, e.getM essageQ , e);
}
} else {
AndroidU tils.alertDialog(this, R .string.erro_ conex ao_ indisponivel);
}
}
}
Lembre-se que a classe AndroidU tils deve ser inserida no projeto
biblioteca que criamos para que durante o. desenvolvimento essas
classes utilitrias sejam consolidadas em um nico projeto, que
futuramente servir de biblioteca para muitas outras aplicaes.
Tambm podemos testar se a conexo est disponvel diretamente na tela do
dashboard e impedir a entrada na tela que lista os carros, se necessrio.
Mai n .java
public class M ain ex tends Activity implements O nClick Listener {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
} __
(SOverride
public void onClick (View v) {
boolean redeO k = AndroidU tils.isNetwork Available(this);
if (redeOk ) {
Intent intent = new Intent(this, TelaListaCarros.class);
// Continua normalmente
} else {
AndroidU tils.alertDialog(tH s, R .string.er ro_ conex ao indisponvel);
}
}
}
Depois dessa alterao podemos testar novamente o aplicadvo, e desta vez, se
nenhuma conexo de dados esriver di spon vel , um alerta ser exibido.
2.12Trabalhando com threads
Cada aplicao Android executa sobre um processo separado dentro do sistema
operacional, e para cada processo criada uma nica thread dedicada, que tem
como principal objetivo controlar a i nterface da tela.
recomendado que sempre que formos executar algum processo demorado
e pesado, como acesso ao banco de dados,, l ei tura de um arquivo ou fazer uma
requisio em um servidor vvet, uma nova chread seja criada para tomar conta
desse processamento. Isso necessrio para que a thread de interface fique livre
para responder aos prximos eventos gerados pelo usurio.
A seguir podemos verificar c exemplo d e busca de carros utilizando correta
mente uma thread separada.
(Si T elaLi staCarros.java
public class TelaListaCarros extends Activity implements Runnable {
private static final String TAG = " li/roandroid-;
private ProgressDialog progresso;
private String tipo;
(W verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedInstanceStat)j
setContentView(R.lay out.carros);
boolean redeO k = AndroidU tils.istetwork Available(this);
if (redeOk) {
tipo = getIntent().getStringE< tra(' ' tipo" );
progresso = ProgressDialog.show(-.his, getString(R.string.app_ name), getString(R.string.aguarde));
Thread t = new Thread(this/' BiscaCarro' ' );
t.start();
} else {
AndroidU tils.alertO ialog(this, R.string.erro_ conex ao_ indisponivel);
}
}
Cap tulo 2 Apli cati vo de exemplo *>1
I
62 Google Android para Tablets
i
(iverride
// M todo da Thread
public void run() {
try {
II Busca os carros em segundo plano
final List<Carro> carros = CarroService.getCarros(this, tipo);
// Ex ecuta o mtodo que atualiz a a View na thread e na interface
runO nU iThread(new RunnableQ {
(SOverride.
public void run() {
for (Carro c : carros) {
Log.i(TAG, "Carro: " + c.nome);
}
}
});
} catch (IO Exception e) {
Log.e(TAG, e.getM essageQ , e);
Androidutils.alertDialog(this, R. string.erro_ io);
} finally {
progresso.dismiss();
}
}
}
Nesse exemplo uma thread iniciada para buscar os carros. Note que antes
disso a classe ProgressDialog utilizada para exibir ao usurio uma janela de pro
gresso e uma mensagem de aguarde, para avis-lo que a transao pode demorar
alguns instantes.
O mtodo run() principal da thread e faz a busca de carros. Existem duas
coisas importantes que precisam ser lembradas aqui.
A primeira que a janela de progresso precisa ser fechada no final da thread,
independente se a execuo funcionar como o esperado ou se acontecer alguma
falha. Para isso o mtodo progresso.dismissQ chamado no bloco finally da thread
para garantir o fechamento dessa janela.
A segunda e mais importante funcionalidade desse exemplo a chamada ao
mtodo runO nliiThread(runnable). Esse mtodo recebe um Runnable que ser execu
tado na thread principal que controla a tela e todo o processo de sua aplicao.
I nternamente esse mtodo utiliza um handler justamente para essa tarefa.
Cap tulo 2 Apli cati vo de exemplo
63
A classe H andler do Android utilizada, entre outras tarefas, para
sincronizar o acesso de uma thread com a thread principal que controla
a interface de usurio. O handler envia uma mensagem para a thread
principal informando que algum processamento precisa ser executado
diretamente nela. Essa mensagem inserida em um tipo de fila. A thread
principal, por sua vez, no momento oportuno, recebe e consome essa
mensagem, retirando-a da fila. Esse fluxo ocorre por debaixo dos panos,
e na verdade voc, como desenvolvedor, deve apenas utilizar a classe
H andler para esses fins. Dessa forma, chamar diretamente o mtodo
runO nU iThread(runnable) como se tivssemos utilizado a classe Handler.
Note que nesse exemplo estamos apenas imprimindo os nomes dos carros no
LogCat, pois ainda no criamos a lista com os carros na tela. Para utilizar os logs
na verdade no era necessrio sincronizar o acesso com a thread principal, mas
fizemos isso para demonstrar o conceito por enquanto, j que a parte visual da
lista ainda no foi criada.
Todo o evento gerado por uma activity, como, por exemplo, a busca de
carros, ou futuramente qualquer interao que a aplicao tenha com
o usurio, precisa responder em at cinco segundos. Esse o limite de
tempo para uma activity. Apenas para constar, um BroadcastReceiver
tambm precisa responder rapidamente, mas este possui dez segundos
de tempo limite. Caso uma activity demore mais de cinco segundos para
responder a um evento, o sistema operacional vai achar que a aplicao
travou e vai exibir o famoso erro Application Not Responding (ANR) com
o boto Force Close, solicitando ao usurio que feche a aplicao, e isso
no nada bom. Portanto, todo processamento demorado, como uma
requisio na internet ou acesso ao banco de dados, por exemplo, deve
ser realizado em uma thread separada, exatamente como fizemos nesse
exemplo.
2.13 A classe AsyncTask
Conforme vimos no exemplo anterior, ao criar uma thread para desvincular a
execuo da thread principal somos obrigados a sincronizar o acesso a todas as
views, pois somente a thread principal tem acesso a elas.
Da surgiu o problema de termos que utilizar um handler ou mtodos de
atalho, como o runO nU iThread(runnable) que vimos anteriormente.
Para auxiliar no desenvolvimento dessa tarefa foi criada a classe Asy ncTask , que
pode ser utilizada para executar uma nova thread e ainda sincronizar o acesso as
views de modo padronizado. Essa classe automaticamente vai executar o processo
em uma thread separada e vai utilizar um handler internamente, encapsulando
todo o trabalho pesado.
64 Google Android para Tablets
Como um cdigo s vezes fala mais que mil palavras, vamos exibir primeira
mente o cdigo de como se utiliza a classe AsyncTask, para posteriormente expli
carmos os detalhes.
BuscaCarrosT ask.java
pack age br.livroandroid.carros.Service;
public class BuscaCarrosTask ex tends Asy ncTask < Void, Void, List< Carro> > {
private static final String TAG = "livroandroid" ;
private Contex t context;
private ProgressDialog progresso;
private final String tipo;
public BuscaCarrosTask (Contex t contex t, String tipo) {
this.contex t = context;
this.tipo = tipo;
}
(SOverride
// Antes de ex ecutar vamos ex ibir uma j anela de progresso
protected void onPreEx ecute() {
progresso = ProgressDialog.show(contex t, contex t.getString(R.string.app_ name),
contex t.getString(R.string.aguarde));
}
// Busca os carros em segundo plano dentro da thread
protected List< Carro> doInBack ground(Void... params) {
try {
List< Carro> carros = CarroService.getCarros(contex t, tipo);
return carros;
} catch (IO Ex ception e) {
Log.e(TAG, e.getM essageQ , e);
AndroidU tils.alertDialog(contex t, R.string.erro_ io);
} finally {
progresso.dismiss();
}
return null;
}
// Atualiz a a view sincroniz ando com a thread principal
protected void onPostEx ecute(List< Carro> carros) {
for (Carro c : carros) {
Log.i(TAG, "Carro: " + c.nome);
}
}
}
Essa classe que fizemos simples, e devemos prestar ateno a trs mtodos
que foram implementados, os quais so definidos na classe Asy ncTask .
Cap tulo 2 Apli cati vo de exemplo 65
M todo__ _ Descrio c:' ' '
onPreEx ecute()
Mtodo executado antes de a thread iniciar, sendo uma boa
oportunidade para exibir a janela de progresso.
doInBack ground()
Mtodo executado sobre uma thread separada e deve conter todo
o processamento pesado. Ele pode retornar um objeto qualquer, o
qual ser passado como parmetro para o mtodo onPostExecute().
onPostEx ecute()
Mtodo executado na thread de interface UI Thread e pode
atualizar os componentes da tela. Ele chamado internamente
udlizando um handler, encapsulando esse trabalho.
Note que nenhum desses mtodos deve ser invocado manualmente, pois eles
so chamados automaticamente pela classe Asy ncTask . Portanto, para iniciar o
processamento necessrio apenas chamar o mtodo Asy ncTask .ex ecute(params...),
informando os parmetros, se necessrio.
Os parmetros genricos dessa classe so definidos ao cri-la. Neste exemplo
a classe foi declarada da seguinte forma:
public class BuscaCarrosTask ex tends Asy ncTask < Void, Void, List< Canro {
Foram declarados trs tipos genricos: Void, Void e List< Carros> .
O primeiro parmetro indica o tipo dos argumentos que sero passados ao
mtodo doInBack ground(). Como neste caso no precisamos informar nenhum par
metro, esse primeiro tipo foi declarado como Void, e consequentemente o mtodo
ficou com essa assinatura dolnBack groiind(Void... params).
O segundo parmetro indica o tipo do objeto que receber notificaes de
progresso, para*que seja possvel visualizar o andamento da execuo. Se esse
parmetro for diferente de Void, como, por exemplo, Integer, poderamos ter im
plementado o mtodo onProgressU pdate(Integer... valores) para receber os valores
do progresso.
O terceiro ltimo parmetro que utilizamos neste exemplo o tipo do objeto
que ser retornado pelo mtodo doInBack ground() e que ser passado diretamente
ao mtodo onPostEx ecute(). Nesse caso o retorno ser a lista de carros representada
por List< Carros> .
Ento vamos novamente alterar a activity que lista os carros para utilizar essa
nova classe que criamos, conforme demonstrado a seguir.
T elaLi staCarros.java
public class TelaListaCarros ex tends Activity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
64 Google An droi d para T ablets
Como um cdigo s vezes fala mais que mil palavras, vamos exibir primeira
mente o cdigo de como se utiliza a classe Asy ncTask , para posteriormente expli
carmos os detalhes.
BuscaCarrosT ask.java
pack age br.livroandroid.carros.Service;
public class BuscaCarrosTask ex tends Asy ncTask < Void, Void, List< Carro> > {
private static final String TAG = "livroandroid" ;
private Contex t context;
private ProgressDialog progresso;
private final String tipo;
public BuscaCarrosTask (Contex t contex t, String tipo) {
this.contex t = context;
this.tipo = tipo;
}
(SOverride
// Antes de ex ecutar vamos ex ibir uma j anela de progresso
protected void onPreEx ecute() {
progresso = ProgressDialog.show(contex t, contex t.getString(R.string.app_ name),
contex t.getString(R.string.aguarde));
}
// Busca os carros em segundo plano dentro da thread
protected List< Carro> doInBack ground(Void... params) {
try {
List< Carro> carros = CarroService.getCarros(contex t, tipo);
return carros;
} catch (IO Ex ception e) {
Log.e(TAG, e.getM essageQ , e);
AndroidU tils.alertDialog(contex t, R.string.erro_ io);
} finally {
progresso.dismiss();
}
return null;
}
// Atualiz a a view sincroniz ando com a thread principal
protected void onPostEx ecute(List< Carro> carros) {
for (Carro c : carros) {
Log.i(TAG, "Carro: " + c.nome);
}
}
}
Essa classe que fizemos simples, e devemos prestar ateno a trs mtodos
que foram implementados, os quais so definidos na classe Asy ncTask .
Cap tulo 2 Apli cati vo de exemplo 65
M todo__ _ Descrio c:' ' '
onPreEx ecute()
Mtodo executado antes de a thread iniciar, sendo uma boa
oportunidade para exibir a janela de progresso.
doInBack ground()
Mtodo executado sobre uma thread separada e deve conter todo
o processamento pesado. Ele pode retornar um objeto qualquer, o
qual ser passado como parmetro para o mtodo onPostExecute().
onPostEx ecute()
Mtodo executado na thread de interface UI Thread e pode
atualizar os componentes da tela. Ele chamado internamente
udlizando um handler, encapsulando esse trabalho.
Note que nenhum desses mtodos deve ser invocado manualmente, pois eles
so chamados automaticamente pela classe Asy ncTask . Portanto, para iniciar o
processamento necessrio apenas chamar o mtodo Asy ncTask .ex ecute(params...),
informando os parmetros, se necessrio.
Os parmetros genricos dessa classe so definidos ao cri-la. Neste exemplo
a classe foi declarada da seguinte forma:
public class BuscaCarrosTask ex tends Asy ncTask < Void, Void, List< Canro {
Foram declarados trs tipos genricos: Void, Void e List< Carros> .
O primeiro parmetro indica o tipo dos argumentos que sero passados ao
mtodo doInBack ground(). Como neste caso no precisamos informar nenhum par
metro, esse primeiro tipo foi declarado como Void, e consequentemente o mtodo
ficou com essa assinatura dolnBack ground(Void... params).
O segundo parmetro indica o tipo do objeto que receber notificaes de
progresso, para*que seja possvel visualizar o andamento da execuo. Se esse
parmetro for diferente de Void, como, por exemplo, Integer, poderamos ter im
plementado o mtodo onProgressU pdate(Integer... valores) para receber os valores
do progresso.
O terceiro ltimo parmetro que utilizamos neste exemplo o tipo do objeto
que ser retornado pelo mtodo doInBack ground() e que ser passado diretamente
ao mtodo onPostEx ecute(). Nesse caso o retorno ser a lista de carros representada
por List< Carros> .
Ento vamos novamente alterar a activity que lista os carros para utilizar essa
nova classe que criamos, conforme demonstrado a seguir.
T elaLi staCarros.java
public class TelaListaCarros ex tends Activity {
@0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
boolean redeO k = AidroidUtils.isNetwork Available(this);
if (redeOk ) {
String tipo = getlntent().getStringEx tra(" tipo" );
new BuscaCarrosTask fthis, tipo).ex ecute();
} else {
AndroidU tils.alertDialog(this, R.string.erro_ conex ao_ indisponivel);
}
}
}
Note que agora o cdigo ficou mais limpo, pois movemos todo o processamento
para a classe BuscaCarrosTask . Como a classe Asy ncTask merece uma ateno especial,
agora vamos explicar detalhadamente o fluxo da execuo.
Primeiramente voc j percebeu que para iniciar o Asy ncTask basta chamar o
mtodo ex ecute(params... ) , conforme podemos visualizar a seguir.
new BuscaCarrosTask (this).ex ecute();
Dentro da classe BuscaC a r r o s T a s k , note que impl ementamos o mtodo
onPreEx ecute() para abrir a janela de progresso, com o objetivo de alertar o usurio
que esse processamento pode demorar alguns instantes. Esse mtodo executado
antes de iniciar uma nova thread.
protected void onPreEx ecuteQ {
progresso = ProgressDialog.show(contex t, contex t.getString(R.string.app_ name),
contex t.getString(R.string.aguarde));
}
Depois disso o mtodo doinBack ground() chamado automaticamente pela classe
Asy nTask em uma thread separada e pode receber os parmetros que voc definiu
na classe. :
Note que nosso mtodo declara um parmetro do tipo Void, indicando que
nenhum parmetro foi passado ao mtodo ex ecuteQ ao iniciar a chamada. Esse m
todo dolnBack ground() est retornando uma lista de carros, declarada como List< Carro> .
protected List< Carro> doInBack ground(Void... params) {
try {
List< Carro> carros = CarroService.getCarros(cantex t);
return carros;
} catch (IO Ex ception e) {
Log.e(TAG, e.getM essageQ , e);
AndroidU tils.alertDialog(contex t, R.string.erro_ io);
66 Google An droi d para T ablets
Cap tulo 2 Apli cati vo de exemplo 67
} finally {
progresso.dismiss();
}
return null;
}
Depois disso a classe Asy ncTask vai automaticamente chamar o mtodo onPostE-
x ecute(), passando como parmetro exatamente a lista de carros que retornamos
no mtodo que executou dentro de uma thread.
O mtodo onPostEx ecute(), por sua vez, oferece garantida de que ser executado
de forma sincronizada com a thread de interface. Nesse momento, se fosse o caso,
poderamos atualizar o ListView com a lista de carros.
protected void onPostEx ecute(List< Carro> carros) {
for (Carro c : carros) {
Log.i(TAG, "Carro: " + c.nome);
}
}
Como vimos, o objetivo da classe Asy ncTask fornecer uma padronizao para
a execuo de tarefas demoradas em uma thread e, posteriormente, a atualizao
da interface. Se no utilizarmos essa classe, ser necessrio executar a thread ma
nualmente e controlar a atualizao da interface utilizando a classe H andler, ou o
mtodo de atalho runO nU iThread(runnable).
Outra vantagem que ao utilizar a classe Asy ncTask podemos utilizar o mto
do cancel(boolean) para encerrar o processamento que est em segundo plano, se
necessrio.
2.14 Criando a interface para a lista de carros com um ListView
Nossa activity j est buscando a lista de carros do servidor e est utilizando threads
corretamente. Portanto, o prximo passo exibir esses carros em uma lista na tela.
Para isso utilizaremos um ListView e tcnicas interessantes que vamos discutir.
Provavelmente a interface de usurio e a lista de carros sejam as partes que voc
estava mais ansioso para verificar, mas antes disso focaremos na implementao
de toda a arquitetura da aplicao e lgica de negcios, como a requisio HTTP,
parser do XML, testes unitrios e a correta utilizao de threads com auxlio da
classe Asy ncTask .
Essa arquitetura precisava estar preparada, pois no Android no podemos de
morar mais que cinco segundos para responder a qualquer evento de tela. Se isso
68 Google An droi d para T ablets
acontecer, o sistema operacional vai exibir a famosa tela com a mensagem A N R
(Application Not Responding), solicitando ao usurio que feche a aplicao.
Agora que j temos uma maneira simples de disparar transaes e buscar a
lista de carros, vamos finalmente partir para a interface de usurio.
Para relembrar o layout da tela vamos verificar as figuras 2.15 e 2.16, que exibem
a lista de carros. Note que na primeira figura as imagens ainda no esto comple
tamente carregadas, mas na segunda figura todos os carros esto sendo exibidos.
Lembre-se que no arquivo XML existe uma URL com a foto de cada carro. Por
isso, a ideia fazermos o download de cada imagem utilizando threads separadas,
enquanto a lista j pode ser visualizada pelo usurio.
. f . s -
r.-'.
}{{! T 535
"CairosShowcase. v:,. .
O
Ferrari FF >
o
AOIGTSpyder >
G
PorschePanamera >
C
Lamborghini Aventador >
m m
Chevrolet CorvetteZOfi >
^ 2 ^
BMWM5 >
*ZJ Renault MeganeRSTrophy
IhffoAndroid'-Todos os direitos fwfvados'
S roartphones e cel ul ares rrnr-.
IG, SompToshiba, Somsung em Vrios Modelos,
Compro cm At 7nS/J uroi! .------- -
UvroAndrid - Todos s direitos reservados
Figuras 2.15 e 2.16 - Lista de carros sendo buscadas dinamicamente da web.
A classe TelaListaCarros utiliza o arquivo de layout /res/layout/carros.xml, que
inicialmente deixamos em branco.
Essa a tela onde vamos inserir o ListView, e o cdigo-fonte completo pode
ser visualizado a seguir.
i /res/layout/carros.xml
<? xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= lhttp://schenias.android.com/apk /res/android
android:orientation= " vertical"
android :lay out_ width= " fillj arent"
1
Cap tulo 2 Apli cati va de exemplo 69
android: layoLrt_height=fill_parenf
android:gravity = " center"
android:back ground= " @ color/fundo >
< include lay out='' glayout/include_header" />
< ListView
android:id=" +id/listview"
android:layout_ width="fill_parent
android:lay out_ height= " 0dp
android:lay out_ weight= " l"
android:cacheColorH int= @ color/transparente"
/>
< include lay out= " @ lay out/indude_ footer" />
< /LinearLay out>
Esse arquivo simples. Apenas insere um cabealho e um rodap na tela e
declara o ListView que vai exibir a lista de carros.
E interessante notar que utilizamos a cor transparente como a cor de cache
do ListView. Se no fizermos isso, ao fazer a rolagem veremos uma cor preta no
fundo, que um problema clssico nos aplicativos para Android.
Lembre-se que no arquivo /res/values/cores.xml foi definida a cor transparente.
Para relembrar podemos visualizar parte do arquivo a seguir.
b /res/values/cores.xml
< ? xml version= " 1.0'encoding="utf-8"? >
< resources>
< color name= " fundo" > # ffffff< /color>
< color name= " transparente" > # 60000000< /color>
< /resources>
2.15 Criando o adapter para a lista de carros
J criamos o arquivo XML de layout com o ListView, agora temos que criar uma
classe que implemente a interface android.widget.Adapter, para popular a lista de carros.
O papel de uma classe Adapter Adaptador fornecer uma view para cada linha
do ListView. Uma classe do tipo Adapter deve implementar mtodos que devem
retornar a quantidade de registros existentes na lista e precisa retornar uma view
para cada linha solicitada. Dessa forma, na linha do carro Ferrari vamos retornar
informaes sobre esse carro. O mesmo vale para outros modelos.
Google Android para Tablets
&
Antes de criarmos a classe Adapter vamos precisar definir o layout de cada linha
da lista. Como cada linha exibe a foto e o nome do carro, vamos precisar criar
um arquivo de layout para isso.
/res/Iayout/carro_i tem.xml
< Pxml version= " 1.0" encoding="utf-8"? >
< F rameLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:layout_width=" fill_parent"
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = " left |center_ vertical"
>
<! -- Lay out com imagem do carro, nome e descrio -->
< LinearLay out
android: lay out_ width= fill_parent" ?
android:lay out_height= " wrap_ content"
android:orientation= " horiz ontal"
android:gravity = " center_ vertical"
android:paddingTop= " 2dp
android :paddingBottom=''2dp"
android:minH eight=" 40dp"
android:layout_marginRight= " 12dp"
>
< F rameLay out
android:lay out_ width=" wrap_content"
android:lay out_ height= wrap_content"
>
<!-- Imagem do carro -->
< ImageView
android:id= @+ id/img"
android: lay out_ width= 11100dp"
android:lay out_height= "50dp"
android:src= "gdrawable/icon"
android:lay out_ gravity = center| center_ vertical"
/>
<!-- Barra de progresso enq uanto carrega a imagem -->
< ProgressBar
android:id= " @ + id/progressn
android:lay out_ width="wrap_content"
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = " center| center_ vertical"
android:gravity = " center| center_ vertical
android:lay out_ marginRight= 6dp"
Cap tulo 2 Apli cati vo de exemplo \
71
sty le= " @ android:sty le/W idget.ProgressBar.Small" />
< /F rameLay out>
< LinearLay out
android:lay out_ width= " wrap_ content"
android:lay out_height=" wrap_content''
android:lay out_ marginRight= " 5dp"
android:orientation= " vertical"
android:lay out_ marginLeft= " 6dp" .
>
<! -- Nome do carro -->
< Tex tView
android:id= " @ + id/tNome"
android:layout_width= "fill_ parent"
android:lay out_ height= " wrap_ content"
android:tex tColor= @ color/preto"
android:text="()string/nome"
sty le= " @ sty le/tex tol4Negrito"
/>
< /LinearLay out>
< /LinearLay out>
<!-- Seta na direita -->
< LinearLay out
android:layout_width=" fill_parent"
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = " right| center_ vertical"
android :gravity= ''right|center_ vertical"
>
<IniageView
android: lay out_ width= " wrap_ content"
android: lay out_ height= " wrap_ content"
android:lay out_ gravity = " right| center_ vertical
android:gravity = right| center_ vertical"
android :src="| 8drawable/seta_ direita"
android:lay out_ marginRight= " 10dp"
/>
< /LinearLay out>
< /F rameLay out>
Nesse arquivo de layout foram definidos uma imagem e um ProgressBar
alinhados esquerda. Note que um FrameLayout foi utilizado para fazer com
que a imagem com a foto do carro e o ProgressBar ficassem no mesmo lugar. Na
prtica, somente um deles ser exibido, e vamos controlar isso com os mtodos
View.setVisibility (View.VISIBLE) e View.setVisibility (View.INVISIBLE).
72
Google An droi d para T ablets
Mais abaixo foi inserido o nome do carro e tambm uma pequena imagem
alinhada direita para criar a seta de navegao para a tela de detalhes. Lembrando
que a imagem da seta pode ser encontrada nos arquivos de download que vm
junto com o livro, assim como o exemplo completo, claro.
Note que o texto Nome est corretamente internacionalizado utilizando o
recurso @ string/nome. Lembre-se que no incio do captulo criamos todas as men
sagens no arquivo /res/values/strings.xml.
O exemplo tambm utiliza o estilo @ sty le/tex tol4Negrito para customizar a fonte
do texto onde aparece o nome do carro.
<!-- Nome do carro -->
< Tex tView
android:tex t= " Nome"
sty le= " @ sty le/tex tol4Negrto"
/>
Apenas para relembrar, esse estilo est criado no arquivo /res/values/css.xml
conforme demonstrado a seguir.
l /res/values/css.xml
< resources>
<! -- Tema padro -->
< sty le name= " tema" >
< item name= " android:windowNoTitle" > true< /item>
< /sty le>
<!-- Tex to com F onte 12 -->
< sty le name= " tex tol2>
< item name= ' ' android:tex tColor" > @ color/preto< /item>
< item name= " android:tex tSiz e" > 12sp< /item>
< /sty le>
< sty le name= " tex tol2Negrito" parent= " tex tol2>
< item name=' 'android:tex tSty le" > bold< /item>
< /sty le>
< !-- Tex to com Fonte 14 -->
< sty le name= " tex tl4>
< item name= 'android:textColor'' >@color/preto</item>
< item name= " android:tex tSiz e" > 14sp< /item>
< /sty le>
< sty le name= " tex tol4Negrito" parent= " tex tol4" >
< item name= " android:tex tSty le" > bold< /item>
< /sty le>
< /resources>
Cap tulo 2 Apli cati va de exemplo 73
Depois de criar o arquivo de layout para cada linha da lista de carros, precisa
mos implementar a classe de Adapter, a qual vai receber a lista de carros e retornar
a view para cada linha.
f e CarroAdapter.java
pack age br.livroandroid.carros.domain;
public class CarroAdapter ex tends BaseAdapter {
protected static final String TA6 = " livroandroid";
private Layoutlnflater inflater;
private final List< Carro> carros;
private final Activity context;
private DownloadlmagemU til downloader;
public CarroAdapter(Activity context, List< Carro> carros) {
this. contex t = context;
this.carros = carros;
this.inflater = (Layoutlnflater) contex t.getSy stemService(Contex t.U Y O U T_ INF LATER_ SERVICE);
downloader = new DownloadlmagemU til(contex t);
}
^ J verride
public int getCount() {
return carros != null ? carros.siz e() : 0;
}
@ 0verride
public O bj ect getltem(int position) {
return carros != null ? carros.get(position) : null;
}
gQ verride
public long getltemld(int position) {
return position;
}
@ 0verride
public View getview(int position, View view, ViewGroup parent) {
ViewH older holder = null;
if (view == null) {
// No ex iste a view no cache para esta linha, ento cria um novo
holder = new ViewH older();
// Busca o lay out para cada carro com a foto
int lay out = R.lay out.carro_ item;
view = inflater.inflate(layout, null);
view.setTag(holder);
holder.tNome = (Tex tView) view.findViewByld(R.id.tNome);
holder.imgF oto = (ImageView) view.findViewByld(R.id.img);
holder.progress = (ProgressBar) view.findViewByld(R.id.progress);
} else {
// 3 ex iste no cache
holder = (ViewHolder) view.getTag();
}
holder.imgF oto.setlmageBitmap(null);
Carro c = carros.get(position);
// Agora que temos a view podemos atualiz ar os valores
holder.tNome.setT ex t(c . nome);
downloader.download(contex t, c.urlF oto, holder. imgF oto, holder.progress);
return view;
}
// Design Patter " ViewHolder" para Android
static class ViewH older {
Tex tView tNome;
ImageView imgF oto;
ProgressBar progress;
}
}
A implementao desse adapter para a lista tranqila, e como o livro voltado
para desenvolvedores Android que j conhecem a plataforma, vamos direto para
as partes mais importantes.
Primeiramente note que foi definida uma classe esttica chamada ViewH older.
static class ViewH older {
Tex tView tNome;
ImageView imgFoto;
ProgressBar progress;
}
Essa classe possui exatamente os campos que precisamos manipular na tela,
definidos no arquivo /res/layout/carroJ t e m . x m l . O motivo para cri-la para que
se possa utilizar cache de views no momento de fazer a rolagem na lista. Di
gamos que na lista sejam exibidos dez carros de cada vez, mas existam trinta
carros disponveis no total. Portanto, ao rolar a tela novas views precisam ser
criadas para cada carro, e sempre que isso for necessrio, naturalmente o mtodo
getView(posicao,view,parent) da classe Adapter ser chamado.
Neste momento naturalmente precisamos retornar uma view que exiba a linha
daquele carro, e para isso vamos utilizar o arquivo de layout /res/layout/carro_item.
xml que criamos anteriormente. O problema que, se criarmos uma nova view toda
vez, isso poder prejudicar o desempenho da rolagem, sendo que no momento
da rolagem diversos objetos estaro sendo criados, consequentemente afetando
muito o tempo de resposta.
74 Google An droi d para T ablets
Portanto, para obter um melhor desempenho ao fazer a rolagem da lista vamos
utilizar o padro ViewHolder, cujo objetivo criar um cache de vievvs para reapro-
veitar os objetos j criados anteriormente e que no esto mais sendo utilizados.
Por exemplo, digamos que estamos exibindo a lista de carros da posio 1 at
o carro 10 e vamos fazer a rolagem para baixo para visualizarmos mais carros.
Dessa forma, se ao rolar a lista aparecerem mais cinco carros, a lista estar exibin
do os carros da posio 5 15 nesse momento. Aqui o pulo do gato! Podemos
reaproveitar as views de 1 a 5 que no esto sendo mais utilizadas, pois no esto
mais visveis na tela, e reaproveit-las para exibir os prximos carros.
Utilizar o padro ViewHolder melhora muito a performance. Se voc
possui uma lista grande de objetos, notar uma diferena incrvel de
velocidade na rolagem da lista ao utilizar esse padro e reaproveitar
- as views j criadas.
Agora, se voc est pensando em como far isso, no se preocupe, pois o
Android vai encapsular a maioria dessa mgica automaticamente. Para ns, de
senvolvedores, cabe apenas o papel de indicar o objeto cujo cache deve ser feito,
e o Android cuidar do resto. Para isso o mtodo getview() possui essa assinatura.
public View getView(int posicao, View view, ViewGroup parent) {
O importante o segundo parmetro, o qual uma view. Esse objeto ser
diferente de nulo sempre que existir um objeto Viewem cache e que puder ser
reutilizado para criar a linha que est sendo solicitada.
Dessa forma, podemos testar se o parmetro view nulo ou no. Se for nulo,
devemos criar o layout normalmente, inflando o arquivo X ML de layout. Nesse
momento preenchemos o objeto ViewH older que far o cache de views para as
prximas linhas.
Mas caso o parmetro view no seja nulo, significa que existe uma view que
est em cache, e naturalmente devemos reaproveit-la para criar a linha.
A seguir podemos visualizar a parte do cdigo-fonte que faz o teste para veri
ficar se o parmetro view nulo ou no.
ViewH older holder = null;
if (view == null) {
// No ex iste a view no cache para esta linha, ento cria ura novo
holder = new ViewH olderQ ;
// Busca o lay out para cada carro com a foto
int lay out = R.lay out.arro_ item;
view = inflater.inflate(layout, null);
Cap tulo 2 Apli cati vo de exemplo 75
76 Google An droi d paraT ablets
view.setTag(holder);
holder.tNome = (TextView) view.findViewByld(R.id.tNome); .
holder.imgF oto = (ImageView). view.findViewByld(R.id.img);
holder.progress = (ProgressBar) view.findViewBy Id(R.id.progress);
} else {
// J ex iste no cache. Bingo! Ento pega!
holder = (ViewHolder) view.getTag();
}
Note que, se no existir a view no cache, o objeto ViewH older criado, caso
contrrio ele recuperado.
O segredo do cache chamar o mtodo setTag(O bj ect) para poder recuperar o
ViewH older posteriormente com o mtodo view.getTag().
Depois de ter o ViewH older preenchido com as views da tela, precisamos atualizar
cada componente com as informaes do carro atual. Nesse momento o nome
do carro atualizado no Tex tView.
Carro c = carros.get(posicao);
// Vamos atualiz ar os valores
holder.tNome.setTex t(c.nome);
Bom, atualizar o nome foi fcil, mas e a foto do carro?
Para a foto temos um problema, porque ela uma URL que contm o endereo
da foto do carro na internet. Portanto, precisamos fazer o download da imagem e
atualiz-la na lista de carros. Se voc observar as figuras que mostramos anterior
mente sobre o exemplo, vai notar que um componente ProgressBar foi utilizado
para exibir uma pequena animao no lugar da imagem do carro enquanto o
download est sendo realizado.
Para fazer o download da imagem poderamos utilizar qualquer cdigo HTTP
padro, desde que, naturalmente, esse download fosse feito em uma thread sepa
rada da thread de interface. Se no utilizarmos uma nova thread para buscar as
fotos, com certeza a performance da lista ficar muito ruim, e a tela ficar pesada
e at poder travar, gerando o clssico erro ANR (Android Not Responding), exibindo
uma janela com aquele outro clssico alerta de F orce Close. Aff! Nada bom, n?
Para evitar esse tipo de problema vamos utilizar uma classe mgica que far os
downloads das imagens de forma automtica e vai exibir um ProgressBar no lugar
da imagem enquanto o download no terminar. O gerenciamento das threads e
da atualizao da imagem com a classe H andler ser feito de forma transparente.
Cap tulo 2 Apli cati vo de exemplo
77
& Down loadlmagemUti l.java
// Esta classe possui ura cdigo muito extenso. Faa o download dos ex emplos do livro
// Voc pode encontrar essa classe no proj eto " LivroAndroid-AndroidU tils"
Depois de baixar o cdigo-fonte dessa classe, copie para o projeto LivroAndroid-
AndroidU tils.
A classe DownloadlmagemU til muito simples de utilizar. Basta criar uma instncia
no construtor de nosso adapter da seguinte forma:
private DownloadlmagemU til downloader;
public CarroAdapter(Activity context., List< Carro> carros) {
this.contex t = contex t;
this.carros = carros;
this.inflater = (Layoutlnflater) contex t.getSy stemService(Contex t.LAY O U T_ INF LATER_ SERVICE);
downloader = new DownloadlmagemU til(contex t);
}
Feito isso no mtodo getView(posicao,view,parent),podemos simplesmente buscar
a foto do carro com uma nica linha de cdigo, e todo o download e a animao
do ProgressBar acontecero automaticamente.
downloader.download(contex t, c.urlFoto, holder.imgF oto, holder.progress);
Note que o mtodo de download recebe o contexto atual, que a activity, a uri
da foto e os componentes I mageView e ProgressBar que vo exibir a foto depois
do download e fazer a animao antes disso, respectivamente. Naturalmente, essas
views precisam existir no arquivo com o layout da linha dos carros, ento apenas
para lembrar vamos exibir novamente o trecho que contm essas duas views.
< FrameLay out
android:lay out_ width= " wrap_ content"
android: lay out_ height= " wrap_ content"
>
<!-- Imagem do carro -->
< ImageView android: id= " @ + id/img"
android:lay out_ width= " 100dp"
android:lay out_ height= " 50dp"
android:src= " @ drawable/icon"
android: lay out_ gravity = " center| center_vertical''
/>
<!-- Animao de progresso enq uanto carrega a imagem -->
< ProgressBar
android:id= " @ + id/progress"
android:lay out_ width= " wrap_ content"
android:lay outJ ieight= wrap_ content"
android:lay out_ gravity = " center| center_ vertical"
android:gravity = " center| center_ vertical"
android:lay out_ marginRight= " 6dp"
sty le= " @ android:sty le/W idget.ProgressBar.Suall"
/>
< /F rameLay out>
Neste caso foi utilizado um FrameLayout para inserir a I mageView e, no mes
mo lugar, o ProgressBar. A classe utilitria que faz o download da imagem vai
utilizar esses dois componentes como mgica e vai exibir um ou outro, conforme
a necessidade.
Outra vantagem dessa classe que internamente ela pode fazer cache no carto
de memria, para evitar que se tenha que acessar a internet e buscar as imagens a
todo momento. Ento, se voc j acessou a aplicao uma vez e buscou as imagens,
elas ficaro salvas no /sdcard e podero ser lidas localmente na prxima vez sem
a necessidade de que uma conexo de dados seja estabelecida. Agora, se voc vai
utilizar ou no esse recurso vai depender da necessidade de seu projeto.
Como essa classe vai precisar salvar dados no carto de memria, necessrio
declarar a permisso W RITE_ EX TERNAL_ ST0RAGE no AndroidManifest.xml da seguinte forma:
< uses-permission android:name= " android.pennission.W RITE_ EX TERNAL_ STO RAGE" />
Note que agora temos trs permisses no arquivo de configurao do projeto.
< uses-permission android:name= " android.permission.INTERNET/>
< uses-permission android:name= " android.permission.ACCE5S_ NETW 0RK _ STATE" />
< uses-permission android:name= " android.permission.W RITE_ EX TERNAt._ ST0RAGE' 7>
Para finalizar, agora que a classe CarroAdapter foi criada, vamos utiliz-la em
nossa activity, assim como o novo arquivo de layout /res/layout/carros.xml que
criamos anteriormente.
Mas para exibir a lista de carros no ListView, ns temos um problema. coisa
simples, mas temos. Ento vamos voltar a verificar o cdigo de nossa activity que
vai exibir a lista de carros.
& T elaLi staCarros.java
public class TelaListaCarros extends Activity {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
boolean redeO k = AndroidU tils.isNetwork Available(this);
Google An droi d para T ablets
Cap tulo 2 Apli cati vo de exemplo
79
if (redeO k ) { &
String tipo = getIntent().getStringEx tra(" tipo" );
new BuscaCarrosTask (this, tipo).ex ecute();
} else {
AndroidU tils.alertDialog(this, R.string.erro_ conex ao_ indisponivel);
}
}
}
O que precisamos fazer utilizar a classe CarroAdapter para popular o ListView.
Mas o pequeno problema que ns criamos a classe BuscaCarrosTask anteriormente
para buscar os carros em segundo plano, lembra-se?
A classe BuscaCarrosTask naturalmente no vai conseguir atualizar o ListView, a
no ser que tenha uma referncia para este. Enfim, no vamos complicar o exemplo
e neste momento vamos juntar essa classe ao arquivo TelaListaCarros novamente,
fazendo com que ela vire uma classe interna (inner class).
O cdigo-fonte final de nossa activity e a atualizao do ListView com a lista
de carros podem ser visualizados a seguir.
m T elaLi staCarros.java
public class TelaListaCarros ex tends Activity impleaents O nltemClick Listener {
private ListView listview;
@ verride
public void onCreate(Bundle savedlnstanceState) {
super. onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
listView = (ListView) findViewByld(R.id.listview);
listView.setO nltemClick Listener(this);
boolean redeO k = AndroidU tils.isNetwork Available(this);
if (redeO k ) {
String tipo = getIntent().getStringEx tra(" tipo" );
new BuscaCarrosTask (this, tipo).execute();
} else {
AndroidU tils.alertDialog(this, R.string.erro_ conex ao_ indisponivel);
}
}
(iverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
// Ao selecionar um carro, vamos ex ibir o seu nome, por enq uanto
Carro c = (Carro) parent.getAdapter().getItem(posicao);
Toast.mak eTex t(this, ''Carro: " + c.nome, Toast.LENGTH _ SH O RT) ,show();
}
<
J
F
C

-
C
A
M
P
U
S

F
O
S


A
L
E
,

'

'
B
I
B
L
I
O
T
E
C
A
80 Google An droi d para T ablets
d a s s BuscaCarrosTask ex tends Asy ncTask < Void, Integer, List< Carro {
private static final String TAG = "livroandroid" ;
private Context context;
private ProgressDialog progresso;
private final String tipo;
public BuscaCarrosTask (Contex t contex t, String tipo) {
this. context = contex t;
this.tipo = tipo;
>
(SOverride
// Antes de executar, vamos ex ibir uma j anela de progresso
protected void onPreEx ecute() {
progresso = ProgressDialog.show(contex t, contex t.getString(R.string.app_ name),
contex t.getString(R.string.aguarde));
}
// Busca os carros em segundo plano dentro da thread
protected List<Carro> doInBack ground(Void... params) {
try {
List< Carro> carros = CarroService.getCarros(contex t, tipo);
return carros;
} catch (IOException e) {
Log.e(TAG, e.getM essage(), e);
AndroidJ tils.alertDialog(contex t, R.string.erro_ io);
} finally {
progresso.dismissO ;
}
return null;
}
// Atualiz a a view sincroniz ando com a thread principal
protected void onPostEx ecute(List< Carro> carros) {
listView.setAdapter(new CarroAdapter(TelaListaCarros.this, carros));
}
}
}
O cdigo-fonte ficou um pouco extenso, pois juntamos a parte que cria o
ListView com a classe BuscaCarrosTask que fizemos anteriormente. Mas fizemos
isso porque classes internas podem acessar normalmente os atributos da classe
principal. Dessa forma a classe BuscaCarrosTask pode atualizar o ListView facilmente
no mtodo onPostEx ecute(carros) que chamado logo depois de a busca ter sido
realizada.
// Atualiz a a view sincroniz ado com a thread principal
protected void onPostEx ecute(List< Carro> carros) {
Cap tulo 2 Apli cati vo de exemplo
81
Activity contex t = TelaListaCarros.this;
listView.setAdapter(new CarroAdapter(contex t, carros));
}
nesse momento que utilizamos a classe CarroAdapter que fizemos anteriormente
para vincular a lista de objetos do tipo Carro que buscamos com o ListView. E falan
do em ListView, note que a activity implementou a interface O nltemClick Listener para
monitorar os eventos da lista e executar uma ao quando um carro for selecionado.
Para isso, implementamos o mtodo onltemClick conforme exibido a seguir.
(SOverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
// Ao selecionar um carro, vamos ex ibir o seu nome, por enq uanto
Carro c = (Carro) parent.getAdapter().getItem(posicao);
Toast.mak eTex t(this, "Carro: " + c.nome, Toast.LENGTH _ SH O RT).show();
}
At o momento o exemplo est apenas exibindo um alerta ao selecionar o
carro. O prximo passo passar o carro selecionado para a prxima tela, para
visualizar os seus detalhes. Mas antes vamos organizar a forma como utilizamos
a classe Asy ncTask .
2.16 Encapsulando o AsyncTask para tornar o cdigo mais simples
Se voc acabou de ler o tpico anterior poder perceber que o cdigo da activity
que exibe a lista de carros ficou um tanto quanto grande. Eu no sei quanto a
vocs, mas estou aqui olhando para ele e no gostei. O autor falando isso sa
canagem, n?
A utilizao da classe Asy ncTask acaba resultando em um cdigo extenso e, para
quem no est acostumado, difcil de entender. Esse cdigo, pelo menos pra mim,
tem sujeira demais.
Ento vamos dar uma limpada no cdigo e apresentar uma ideia para execu
tar esse tipo de busca, que eu costumo chamar de transao. Se a classe Asy ncTask
encapsula a criao de uma thread e a utilizao de um H andler, agora ns que
vamos encapsular o cdigo dessa Asy ncTask para tornar a nossa vida de desenvol
vedor mais fcil.
A sugesto criar uma classe Transacao, que possui o mtodo ex eeutar() para
fazer a operao que for necessria e o mtodo atualiz arViewQ para atualizar a tela.
O mtodo ex ecutar() chamado sobre uma nova thread, e o mtodo atualiz arView()
naturalmente j est sincronizado com a thread de interface.
82 Google An droi d para T ablets
i
Portanto, vamos criar no projeto LivroAndroid-AndroidU tils uma nova interface e
uma classe para aumentar cada vez mais a nossa biblioteca de classes utilitrias.
i T ran sacao.java
pack age br.livroandroid.transacao;
public interface Transacao {
// Ex ecutar a transao em uma thread separada
public void ex ecutar() throws Exception;
H Atualiz ar a view sincroniz ada com a thread de interface
public void atualiz arViewQ ;
}
Para controlar a interface Transacao e utilizar um Asy ncTask vamos criar a classe
TransacaoTask , a qual far de forma centralizada todo o trabalho pesado.
Essa classe filha de Asy ncTask e recebe uma implementao da interface Tran
sacao em seu construtor, para delegar a execuo dos mtodos para essa interface.
) T ran sacaoT ask.java
pack age br.livroandroid.transacao;
public class TransacaoTask ex tends Asy ncTask cVoid, Void, Boolean> {
private static final String TAG = " livroandroid";
private final Contex t context;
private final Transacao transacao;
private ProgressDialog progresso;
private Throwable ex ceptionErro;
private int aguardeHsg;
public TransacaoTask (Contex t context, Transacao transacao, int aguardeHsg) {
this.contex t = contex t;
this.transacao = transacao;
this.aguardeH sg = aguardeHsg;
}
gO verride
protected void onPreEx ecute() {
super.onPreEx ecute();
// Inicia a j anela de aguarde...
abrirProgress();
}
(JOverride
protected Boolean doInBack ground(Void... params) {
try {
transacao.ex ecutar();
} catch (Throwable e) {
Cap tulo 2 Apli cati vo de exemplo
83
Log.e(TAG, e.getM essage(), e);
// Salva o erro e retorna false
this.ex ceptionErro = e;
return false;
} finally {
try {
fecharProgress()j
} catch (Ex ception e) {
Log.e(TAG, e.getM essage(), e);
}
}
// Ok
return true;
}
gO verride
protected void onPostEx ecute(Boolean ok) {
if (ok) {
// Transao ex ecutou com sucesso
transacao.atualiz arView();
} else {
// Erro
AndroidU tils.alertDialog(contex t, "Erro: " + ex ceptionErro.getM essageQ );
}
}
public void abrirProgressQ {
try {
progresso = ProgressDialog.show(contex t, contex t.getString(aguardeM sg));
} catch (Throwable e) {
Log.e(TAG, e.getM essage(), e);
}
}
public void fecharProgress() {
try {
if (progresso != null) {
progresso.dismiss();
}
} catch (Throwable e) {
Log.e(TAG, e.getM essage(), e);
}
}
}
Essa classe possui um cdigo relativamente complexo, mas voc talvez nunca
mais precise alter-la. Basicamente, essa classe um Asy ncTask , mas est delegando
as chamadas para a interface Transacao com os mtodos ex ecutarQ e atualiz arViewQ
automaticamente.
84 Google An droi d para T ablets
O prximo passo utilizar essa nova classe para executar as transaes, mas antes
vamos criar uma nova activity, que ser a me de todas as activities de nosso projeto,
a fim de ter alguns mtodos utilitrios, como, por exemplo, iniciar uma transao.
i f e Li vroAn droi dActi vi ty.java
pack age br.livroandroid.carros.activity;
public class LivroAndroidActivity ex tends Activity {
protected void alert(int mensagem) {
AndroidU tils.alertDialog(this, mensagem);
>
// Inicia a thread
public void startTransacao(Transacao transacao) {
boolean redeO k = AndroidU tils.isNetwork Available(this);
if (redeO k) {
// Inicia a transao
TransacaoTask task = new TransacaoTask (this, transacao , R.string.aguarde);
task .ex ecuteQ ;
} else {
// No ex iste conexo
AndroidU tils.alertDialog(this, R.string.erro_ conex ao_ indisponivel);
}
}
}
Note que podemos criar vrios mtodos utilitrios nessa classe, mas o que nos
interessa agora o mtodo startTransacao(transacao). Esse o mtodo que devemos
chamar em nossa activity para iniciar alguma tarefa.
Por ltimo vamos alterar novamente a classe que lista os carros para utilizar
nossa nova arquitetura. Repare que vamos implementar a interface Transacao que
acabamos de criar.
public class TelaListaCarros ex tends LivroAndroidActivity implements O nltemClick Listener,
Transacao {
) T elaLi staCarros.java
pack age br.livroandroid.carros.activity ;
public class TelaListaCarros ex tends LivroAndroidActivity implements O nltemClick Listener, Transacao
{
private ListView listView;
private List< Carro> carros;
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
Cap tulo 2 Apli cati vo de exemplo 85
setContentView(R.lay out.carros);
listView = (ListView) findViewById(R.id.listview);
listView.setO nltemClick Listen r(this);
// Simples assim...
startTransacao(this);
}
gO verride
public void ex ecutar() throws Ex ception {
II Busca os carros em uma thread
this.carros = CarroService.getCarros(this, Carro.TIP0_ ESP0RTIV0S);
}
Override
public void atualiz arView() {
// Atualiz a os carros na thread principal
if (carros != null) {
listView.setAdapter(new CarroAdapter(this, carros));
}
}
(SOverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
// Ao selecionar um carro, vamos ex ibir o seu nome, por enq uanto
Carro c = (Carro) parent.getAdapter().getItem(posicao);
Toast.mak eTex t(this, "Carro: " + c.nome, Toast.LENGTH _ SH O RT).show();
}
}
E isso tudo. Simples assim!
Note que esse cdigo faz a mesma coisa que a classe anterior, mas agora est
limpo, e creio que voc, assim como eu, consegue entender.
Perceba que, como encapsulamos todo o processamento dentro da classe me
LivroAndroidActivity, ela possui o mtodo startTransacao(transacao), que automaticamente
dispara um AsyncTask e vai executar os mtodos executar() e atualizarView() para voc.
Com esta infraestrutura de transaes pronta voc ficar tranqilo e poder
reutiliz-la em outros projetos.
2.17 Exibindo os detalhes do carro selecionado
Vamos agora desenvolver a funcionalidade de selecionar um carro e pass-lo para
a prxima tela, onde os detalhes podem ser exibidos.
Lembre-se que j estamos monitorando os eventos da lista e recuperando o
carit) selecionado, mas estamos apenas exibindo um alerta na tela.
86
Google An droi d para T ablets
@ O verride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
/ / A o selecionar ura carro, vamos ex ibir o seu nome, por enq uanto
Carro c = (Carro) parent.getAdapter().getItem(posicao); '
Toast.mak eTex t(this, "Carro: " + c.nome, Toast.LENGTH _ SH O RT).show();
}
O prximo passo alterar esse mtodo para chamar a prxima tela, passando
o carro selecionado como parmetro, conforme podemos visualizar a seguir.
gO verride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapter().getItem(posicao);
Intent intent = new Intent(this, TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , c);
startActivity(intent);
}
Note que estamos passando como parmetro para a prxima tela o carro
selecionado como um Serializ able. Sendo assim, vale lembrar que a classe Carro
j implementa essa interface. Basicamente, a classe Serializ able utilizada para
converter um objeto para formato binrio e vice-versa.
(S; Carro.java
pack age br.livroandroid.carros.domain;
public class Carro implements Serializ able {
private static final long serialVersionU IO = 6601006766832473959L;
public static final String K EY = "carro";
public static final String TIPO = "tipo";
public static final String TIP0_ CLASSIC0 = "clssicos";
public static final String TIPO J SPO RTIVO S = "esportivos";
public static final String TIP0_ LU X 0 = "luxo";
public String nome;
public String desc;
public String urlfoto;
public String urllnfo;
}
Note que a constante k e y definida na classe Carro utilizada como o nome do
parmetro, e para ler o carro na prxima tela basta utilizar o seguinte mtodo:
carro = (Carro) getlntentQ .getSerializ ableEx tra(Carro.K EY );
Vamos continuar o exemplo e criar a tela de detalhes conforme demonstrado
a seguir.
f e T elaDetalhesCarro.java
pack age br.livroandroid.carros.activity ;
public class TelaDetalhesCarro ex tends Activity implements O nClick Listener {
private static final String TAG = " livroandroid";
private Carro carro;
gO verride
protected void onCreate(8undle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out. carro_ detalhes);
carro = (Carro) getIntent().getSerializ ableEx tra(Carro.K EY );
Log.i(TAGj " Ex ibindo carro: " + carro.nome);
Tex tView tTitulo = (Tex tView) findViewByld(R.id.tH eader);
Tex tView tDesc = (Tex tView) findViewByld(R.id.tDesc);
tTitulo.setTex t(carro.nome);
tDesc.setTex t(carro.desc);
// L a imagem do cache
II Downloadlmagemlltil possui um H ashM ap interno. Chave=URL
ImageView img = (ImageView) findViewByld(R.id.img);
Downloadlmagemlltil downloader = new DownloadlmagemU til(this);
Bitmap bitmap = downloader.getBitmap(carro.urlF oto);
if (bitmap != null) {
img.setlmageBitmap(bitmap);
}
// Site
Button btSite = (Button) findViewById(R.id.btAbrirSite);
btSite.setO nClick Listener(this);
}
gdverride
public void onClick (View view) {
String uri = carro.urllnfo;
startActivity (new Intent(Intent.ACTIO N_ VIEW , Uri.parse(url)));
}
}
A seguir podemos visualizar o arquivo de layout.
#1 /res/layout/carro_detalhes.xml
< ? xml version= " 1.0 encoding= " utf-8" ? >
< LinearLay out x mlns:android= http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android: lay out_ width= fill_parent''
android:lay out_height=" fill_parent"
android:gravity = " center"
Cap tulo 2 Apli cati vo de exemplo j
android:back ground= " @ color/fundo >
< include lay out= " @ lay out/include_ header" />
< LinearLay out
android:lay out_ width= " fillj > arent
android:lay out_ height= " 0dp"
android:padding= " 10dp"
android:orientation= " vertical"
android:lay out_ weight= " l"
>
< ImageView
android: id=" g+id/img"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = center"
/>
< View
android:lay out_ width= " match_ parent"
android:lay out_ height= ldp"
android:back ground= " # dddddd"
android:lay out_ marginTop= " 10dp"
android:lay out_ marginBottom= " 10dp"
/>
< ScrollView
android:lay out_ width= " match_ parent
android: lay out_ height= " wrap_ content"
>
< Tex tView
android:id= " @ + id/tDesc
android: layout_width="fill_parent"
android:lay out_ height= " wrap_ content
sty le= " @ sty le/tex tol4
/>
< /ScrollView>
< /LinearLay out>
< Button
android:id= " @ + id/btAbrirSite"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= "(J string/abrirSite"
android:lay outj nargin= 4dp"
android:back ground= " @ drawable/btn_ normal"
android:tex tColor= " @ color/preto"
/>
< include lay out= glay out/include_ footer" />
< /LinearLay out>
Cap tulo 2 Apli cati vo de exemplo 89
No se esquea de declarar a activity no AndroidManifest.xml.
< activity android:name= " .activity .TelaDetalhesCarro" />
Feitas estas implementaes, j podemos executar o exemplo e selecionar um
carro na lista, conforme a figura 2.17.
O arquivo de layout exibe uma imagem, a descrio do carro e um boto para
abrir o site do fabricante com mais detalhes.
Primeiramente o carro selecionado recuperado pelo parmetro passado pela
Intent, e depois o Tex tview com a descrio do carro atualizado.
carro = (Carro) getIntent().getSerializ ableEx tra(Carro.K EY );
Tex tview t = (Tex tView) findviewById(R.id.text);
t.setTex t(carro.desc);
Feito isso, necessrio exibir a imagem do carro selecionado. Como o down
load dessa imagem foi feito pelo adapter da lista de carros, podemos novamente
utilizar a classe DownloadlmagemU til para recuperar a imagem.
Mas desta vez vamos utilizar apenas o mtodo getBitmap(url), que vai simples
mente buscar essa imagem do cache, sem que haja a necessidade de fazer um novo
download e acesso internet.
// L a imagem do cache
ImageView img = (ImageView) findViewByld(R.id.img);
DownloadlmagemU til downloader = new DownloadlmagemU til(this); a
A Ferrari FF acaba de ser revelada. Se trata do
primeiro modelo da marca a ter trao integral.
Alm disso, ele conta com ummotor dianteiro
V12. Se trata de ummodelo GT de quatro
lugares que no s substitui a612 mas tambm
atrai umnovo tipo de cliente, daquele que gosta
de percorrer caminhos mais difceis que exigem
trao integral.
ste modelo revolucionrio (dentro da marca)
tem umnovo chassi com entre-eixos maior,
alm de suspenso independente que incorpora
a ltima gerao de amortecedores ajustveis,
alm de freios de cermica da Brembo.
Figura 2.17 - Detalhes do carro selecionado.
90 Google An droi d para T ablets
b
Bitmap bitmap = downloader.getBitmap(carro.urlF oto);
if (bitmap != null) {
img.setlmageBitmap(bitmap);
}
Note que tambm adicionamos um evento no boto que vai abrir o site do
fabricante, e isso foi implementado da seguinte forma:
(SOverride
public void onCreate(Bundle savedlnstanceState) {
// Site
Button btSite = (Button) findViewByld(R.id.btAbrirSite);
btSite.setO nClick Listener(this);
}
(SOverride
public void onClick (View view) {
String uri = carro.urllnfo;
startActivity (new Intent(Intent.ACTIO N_ VIEW , U ri.parse(url)));
}
Perceba que ao abrir o site do fabricante, no somos mais responsveis pelo
contedo do site e, consequentemente, no podemos garantir que este estar
customizado para dispositivos mveis.
A figura 2.18 exibe o site do fabricante Ferrari.
j I I http://www.ferrari.com/... 'j P ;
Figura 2.18 - Site do fabricante aberto.
Para finalizar este tpico vamos observar um ltimo e importante detalhe, que
a imagem utilizada como fundo para o boto de abrir o site.
<Button
android: id="( +id/btAbrirSite"
android:tex t= " @ string/abrirSite"
android:back ground= " @ drawable/btn_ normal" />
As imagens do boto normal e pressionado foram inseridas na pasta Iresl
drawable-mdpi, de mdia resoluo, e sero utilizadas para todas as diferentes
densidades e tamanhos de telas.
Os nomes das imagens so btn_normal_off.9.png e btn_normal_on.9.png, respec
tivamente. Note que a extenso da imagem termina com .9.png, e esse formato
conhecido como NinePatch ou 9-patch. Basicamente, esse tipo de imagem
utilizada quando o contedo ou tamanho da view desconhecido e queremos
esticar a imagem sem distorc-la.
Dessa forma, como escrevemos Abrir o Site no boto, essas imagens foram
redimensionadas sem distoro, para criar corretamente a imagem de fundo do
boto, o que garante a qualidade de nossa interface grfica.
Para saber mais sobre as imagens 9-patch acesse a documentao oficial do
Android e verifique a ferramenta Draw 9-patch, que permite criar esse tipo de
imagem.
Como temos as imagens para o estado normal e pressionado, vamos criar outro
arquivo XML e inserir na pasta /res/drawable. Esse XML representa a imagem final
que eleve ser utilizada como fundo para o boto.
f e /res /dra wa ble/bt n _n o rma|.x ml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< selector x mlns:android= " http://schemas.android.com/apk /res/android">
<!-- Efeito especial de on/off -->
< item android:drawable= " @ drawable/btn_ normal_ off" android:state_ pressed= ' lfalse''
android:state_ focused= " false" />
<item android :drawable= " @ drawable/btn_ normal_ off" android:state_ pressed= " false"
android:state_ focused= " true" />
< item android:drawable= " @ drawable/btn_ normal_ on" android:state_ pressed= " true"
android:state_ focused= false" />
< /selector>
A figura 2.19 exibe as imagens 9-patch utilizadas para o estado normal e pres
sionado do boto.
Cap tulo 2 Apli cati vo de exemplo 91
92 Google An droi d para T ablets
Figura 2.19 - Imagens 9-patch no estado normal e pressionado.
2.18 Exibindo a tela de sobre com um WebView
Na tela inicial do dashboard temos os trs botes para visualizar os carros e um
quarto boto com o texto Sobre para visualizar os detalhes do aplicativo, con
forme podemos visualizar na figura 2.20.
Este livro dedicado aos
desenvolvedores Android que desejam
aprimorar seus conhecimentos e estudar
as novas funcionalidades presentes no
Android 3j (, como fragments e
actionbar.

LfvroAnorod Todos direitos reservados


Figura 2.20 - Tela de sobre com W ebView.
Para criar essa tela vamos utilizar um WebView e carregar a seguinte pgina.
http://www.livroandroid.com.br/livro/carros/sobre.htm
A seguir podemos visualizar a implementao dessa tela, que apenas chama
esse site no WebView.
iS d T elaSobre.java
pack age br.livroandroid.carros .activity;
public class TelaSobre ex tends Activity {
Cap tulo 2 Apli cati vo de exemplo 93
private static final String TAG = " livroandroid" ;
private static final String U RL_ S0BRE = "http://www.livroandroid.coin.br/livro/carros/sobre.htni";
@ O verride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.sobre);
W ebView webview = (W ebView) findViewBy Id(R.id.webview);
W ebSettings settings = webview.getSettings();
// Ativa o J avaScript na pgina
settings.set] avaScriptEnabled(true);
// Publica a interface para o J avaScript
webview.addJ avascriptInterface(this, "LivroAndroid");
// Abre a pgina
webview. loadllrl(URL_SO BRE);
}
}
Essa classe utiliza um arquivo de layout com um WebView, o qual recuperado
durante a inicializao da activity para informar a URL que contm a pgina. A
seguir podemos visualizar o arquivo de layout.
& /res/layout/sobre.xml
<? xml version= " 1.0 encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android:layout_width="fill_parent"
android:lay out_ height= " fill_ parent"
android:gravity = " center"
android:back ground= " @ color/fundo"
>
< include lay out= " @ lay out/include_ header/>
<W ebView
android: id= @+icl/webview"
android:layout_width=" fill_parent"
android:lay out_ height= " 0dp"
android:lay out_ weight= " l" />
< include lay out= @ lay out/include_ footer'' />
< /LinearLay out>
Essa tela bem simples e apenas exibe a URL com as informaes do carro
em um webview para aproveitar uma pgina existente da internet. Poderamos,
claro, ter feito um layout de tela manual com as informaes do carro, mas o
objetivo aqui foi demonstrar esse componente WebView, que amplamente uti
lizado hoje em dia em aplicativos para smartphones e tablets.
Lembre-se que precisamos declarar essa nova activity no AndroidManifest.xml.
activity android:name= .activity .TelaSobre" />
A seguir podemos verificar como ficou o arquivo AndroidManifest.xml no final
deste exemplo.
i An droi dMan i f est.xml
<?xml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.com/apk /res/android"
pack age= " br.livroandroid.carros"
android:versionCode= " l"
android:versionName= " 1.0" >
< uses-sdk android:minSdk Version= " 4" />
< uses-permission android :name='android.perniission. INTERNET" />
< uses-permission android:^ alne= " and^ oid.pe^ nission.ACCESS_ NETW O RK _ STATE,' />
< uses-permission android:name= " android.permission.W RITE_ EX TERNAL_ STO RAGE" />
<application
android:icon= " @ drawable/icon"
android: label=" lstring/app_naine"
android:theme="@)style/tenia" >
activity
android: label= string/app_name''
android:name= " .M ain" >
<intent-filter >
<action android:name= android.intent.action.H AIN/>
<category android:narae="android.intent.category .LAU NCH ER" />
</intent-filter>
< /activity >
<activity android:name= " .activity .TelaListaCarros" />
<activity android:name= " .activity .TelaDetalhesCarro" />
<activity android:naue= " .activity .TelaSobre/>
< /application>
< /manifest>
94 Google An droi d para T ablets
2.19 Integrando J ava e J avaScript no WebView
A classe W ebView possui muitas funcionalidades e pode fazer muito mais do que
simplesmente exibir uma pgina da internet. A pgina html exibida poderia estar
embutida na aplicao ou no carto de memria, por exemplo.
Cap tulo 2 Apli cati vo de exemplo
95
Mas o WebView um componente completamente customizvel, e podemos
utilizar alguns de seus mtodos para alterar o comportamento da pgina. Por exem
plo, podemos utilizar ou no o cache e habilitar o J avaScript da seguinte forma:
W ebSettings settings = webview. getSettingsQ ;
settings.setCacheM ode(W ebSettings.LO AD_ NO _ CACH E);
settings.setH avaScriptEnabled(true);
Existem muitas funcionalidades que podem ser interceptadas. Por exemplo,
um alerta J avaScript pode ser interceptado para exibir uma alerta com um To-
ast, mtodos podem ser implementados para receber erros ao carregar a pgina,
interceptar os comandos de voltar e avanar, e possvel at chamar mtodos no
J ava utilizando funes J avaScript na pgina html.
Para demonstrar como utilizar funes J avaScript no WebView foi criado um
exemplo especial para o livro: http://wwiv.livroandroid.com.br/livro/carros/sobre.htm
Note que no cdigo-fonte dessa pgina est definida a seguinte funo J avaScript.
< script ty pe= " tex t/j avascript" >
function voltarJ SQ {
// F uno J avaScript q ue chama o m todo na activity
LivroAndroid.voltar();
}
</script>
E a funo chamada normalmente na pgina web.
< img src= " img/voltar.png" onClick = " voltar3S();" />
Agora perceba que a funo J avaScript utiliza um objeto chamado LivroAndroid,
o qual possui um mtodo voltarQ . Esse objeto pode ser definido na activity e
publicado com esse nome para que ele possa ser utilizado na pgina web.
Para isso, na activity precisamos definir essa interface chamada LivroAndroid da
seguinte forma, onde this representa a classe que possui os mtodos que sero
disponibilizados ao J avaScript. Neste caso, this a prpria activity
webview.add:avascriptlnterface(this, " LivroAndroid);
E para finalizar, precisamos definir apenas o mtodo voltarQ que ser utilizado
pelo J avaScript.
// M todo chamado pela pgina html no W ebview
public void voltarQ {
Log.i(TAG, " voltarQ " );
finishQ ;
}
96 Google An droi d para T ablets
Note que essa uma funcionalidade muito poderosa, e com isso podemos
mixar pginas html carregadas em um WebView com chamadas aos mtodos em
J ava. Obviamente existem casos em que isso ser til ou no, mas^stamos apenas
demonstrando a funcionalidade aqui, e caber a voc decidir como utilizar isso
em seu projeto, se for necessrio.
A seguir podemos visualizar o cdigo final da classe que exibe o WebView e
publica a interface LivroAndroid para ser utilizada pelo J avaScript. Note que o m
todo v o l t a r Q definido nessa classe chamado ao clicar no boto voltar da pgina
dentro do WebView.
i T elaSobre.java
public class TelaSobre extends Activity {
private static final String TAG = "livroandroid";
private static final String U RL_S0BRE = "http://www.livroandroid.coni.br/livro/carros/sobre.htin";
@ 0verride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.sobre);
W ebview webview = (WebView) findViewById(R.id.webview);
W ebSettings settings = webview.getSettingsQ ;
// Ativa o J avaScript na pgina
settings.set] avaScriptEnabled(true);
// Publica a interface para o J avaScript
webview.addJ avascriptInterface(this, " LivroAndroid" );
// Abre a pgina
webview.loadU rl(U RL_ SO BRE);
}
// M todo chamado pela pgina html no webview
public void voltarQ {
Log.i(TAG, " voltarQ " );
// Ento encerramos a activity para demonstrar
uma boa prtica de interface para Android no utilizar botes de
voltar dentro do aplicativo e deixar que o bot fsico de voltar seja: .7
utilizado como padro. Neste exemplo utilizamos o voltar dentro"1--
do WebView apenas para.exercitar os conceitos e a integrao do--,
cdigo da activity com o J avaScript. i-
finishQ;
}
}
Cap tulo 2 Apli cati vo de exemplo 97
2.20 Exibindo um ProgressBar enquanto o WebView no carrega
O exemplo at agora timo, mas caso a conexo de dados esteja lenta e a pgina
com os detalhes do carro demore muito para carregar, o usurio vai visualizar
uma tela vazia enquanto isso.
Portanto, vamos dar um acabamento mais profissional ao exemplo e inserir um
ProgressBar no centro da tela, para indicar que o WebView est sendo carregado.
E quando a pgina terminar de carregar, vamos esconder o ProgressBar.
Ento, primeiramente vamos alterar o layout da tela que possui o WebView e
adicionar um ProgressBar em cima dele. Isso feito utilizando-se um FrameLayout,
que um gerenciador de layout que exibe um componente em cima do outro.
(s /res/layout/sobre.xml
<?x ml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= http://schemas.android.com/apk /res/android
android:orientation= " vertical"
android:layout_ width=" fill_parent"
android:lay out_ height="fill_parent
android:gravity = " center"
android:back ground= @ color/fundo
>
nclude lay out= " glay out/include_ header" />
< F rameLay out
android:layout_ width= "fill_ parent"
android:lay out_ height= 0dp"
android:lay out_ weight= " l"
>
<W ebView
android:id= " @ + id/webview"
android:lay out_ width= fill_parent"
android:layout_height= "fill_ parent"
/>
< ProgressBar
android:id= " @ + id/progress
android:lay out_ width= " wrap_ content
android: lay out_ height= " wrap_ content"
android:lay out_ gravity = " center" />
< /F rameLay out>
< include lay out= @ lay out/include_ footer" />
< /LinearLay out>
98
Google An droi d para T ablets
Por padro, o ProgressBar exibe uma pequena animao automaticamente,
sem que seja preciso ativ-lo, conforme a figura 2.21.
A tela de detalhes exibe agora um ProgressBar enquanto o WebView est
carregando a pgina, mas o problema agora que ele fica l para sempre. Ento
devemos monitorar o carregamento da pgina do WebView para esconder o
ProgressBar no final.
Felizmente, isso simples de se fazer no Android. Basta utilizar uma imple
mentao customizada da classe android.webk it.W ebViewClient da seguinte forma:
private void monitoraW ebView(W ebView webview) {
webview.setW ebViewClient(new W ebViewClientQ {
@ verride
public void onPageStarted(W ebView view, String uri, Bitmap favicon) {
super.onPageStarted(view, uri, favicon);
log.i(TAG, "onPageStarted" );
ProgressBar progress = (ProgressBar) findView8yId(R.id.progress);
progress.setVisibility (View. VISIBLE);
}
(SOverride
public void onPageF inished(W ebView view, String uri) {
super.onPageF inished(view, uri);
Log.i(TAG, "onPageF inished" );
ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
Li vro Googl e
Androi d
nrDROiD
as novas funcionalidades presentes no
Android 3.x, como fragments e
actionbar.

Figura 2.21 - Tela de sobre com ProgressBar no W ebView.


Captulo 2 Aplicativo de exemplo 99
&
progress.setVisibility (View.INVISIBLE);
}
});
}
Utilizando um WebViewClient podemos monitorar o incio e o fim do carrega
mento da pgina e, principalmente no fim, podemos esconder a animao do
ProgressBar facilmente.
A seguir podemos visualizar o exemplo completo da tela de sobre.
n T elaSobre.java
pack age br.livroandroid.carros.activity ;
public class TelaSobre ex tends Activity {
private static final String TAG = " livroandroid" ;
private static final String U RL_ S0BRE = "http://www.livroandroid.coj i.br/livro/carros/sobre.htni";
(SOverride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.sobre);
W ebview webview = (W ebView) findViewBy ld(R.id.webview);
W ebSettings settings = webview.getSettings();
// Ativa o J avaScript na pgina
settings.setJ avaScriptEnabled(true);
// Publica a interface para o J avaScript
webview.add] avascriptlnterface(this, " LivroAndroid" );
// Abre a pgina
webview.loadU rl(U RL_ SO BRE);
monitoraW ebView(webview);
}
private void monitoraW ebView(W ebView webview) {
webview.setW ebViewClient(new W ebViewClient() {
g verride
public void onPageStarted(W ebView view, String uri, Bitmap favicon) {
super.onPageStarted(view, uri, favicon);
Log.i(TAG, " onPageStarted" );
}
@ O verride
public void onPageF inished(W ebView view, String uri) {
super.onPagefinished(view, uri);
Log.i(TAG, " onPageF inished" );
ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
progress.setVisibility (View.INVISIBLE); *
}
700 Google An droi d para T ablets
// M todo chamado pela pgina html no W ebView
public void voltarQ {
Log.i(TAG, " voltarQ " );
// Ento encerramos a activity para demonstrar
finish();
}
}
2.21 Consideraes sobre o exemplo
Esse foi um exemplo simples, mas apresentou diversos conceitos importantes para
o desenvolvimento de aplicaes para o Android.
O exemplo abordou a busca de uma lista de carros na internet, o parser do
XML, a correta utilizao de threads e a atualizao de um ListView utilizando
o padro ViewH older. Vimos um interessante componente para fazer o download
das imagens na lista enquanto um ProgressBar exibido, e o resultado foi uma
aplicao que at que tem o seu valor.
Algumas explicaes foram corridas devido ao fato de este livro ser focado em
auxiliar desenvolvedores Android que j conhecem a plataforma. Dessa forma,
esse exemplo foi apresentado com o objetivo de servir como base para nossos
prximos captulos, onde iremos abordar alguns assuntos importantes, como, por
exemplo, a migrao passo a passo deste projeto para tablets.
Este projeto foi criado rapidamente com o objedvo de servir como
base para as explicaes dos prximos captulos, que vo fornecer
solues e boas prticas de desenvolvimento para Android, alm de
j apresentar novas APIs.
Ainda existe muito que melhorar nesse aplicativo, e apenas para ter uma ideia
de um dos problemas, faa o seguinte teste:
Depois de entrar no aplicativo e ter visualizado a lista de carros com a tela do
celular na vertical, tente posicionar o celular na horizontal e ver o que acontece.
Voc perceber que a lista de carros foi novamente carregada da internet, assim
como suas imagens (ou lidas do cache feito no carto de memria).
Isso pode no ser o desejado, uma vez que gerou mais trfego de rede, consu
mindo mais recursos, e at porque pode demorar novamente para carregar a tela.
Outra dvida seria: e se fosse necessrio criar um layout XML diferenciado
para quando o celular est na vertical ou na horizontal, como faramos?
As respostas para essas questes esto no prximo captulo. At logo!
CAPTULO 3
Controlando o estado de
sua activity
Uma activity representa uma tela d a aplicao em que o usurio est, de alguma
forma, interagindo com o sistema. E n tre outras responsabilidades, a activity precisa
controlar a interface da tela e seus eventos.
Como desenvolvedores, precisa, m o s estar atentos ao ciclo de vida de uma
activity e sempre salvar o seu estado corretamente, para o caso de o processo da
aplicao vir a ser interrompido poir outro aplicativo ou por alguma alterao nas
configuraes de sistema que v arras estudar neste captulo.
3.1 Vertical e horizontal
Smartphones modernos com o si stema operacional Android possuem o sensor
integrado e o usurio f requentemente gira o celular para a horizontal (deitado)
ou vertical (em p) para obter uma mel hor visualizao do aplicativo.
Esse processo de girar o celular conhecido como troca de orientao. Para
seu desenvolvimento vamos uti l i zar a palavra portrait para a vertical e landscape
para a horizontal.
-Por exemplo; o usurio pode estar com o celular na vertical e navegando no
Y ouTube procura de vdeos i nteressantes, e de repente ele escolhe um vdeo e
comea a assisti-lo. Usurios j acos tumados aos recursos do Android iro rapi
damente posicionar o celular na hori zontal para obter uma melhor visualizao
do vdeo e aproveitar melhor o espao na tela, uma vez que layouts diferentes so
exibidos para cada orientao.
101
6.3 Como controlar o contedo de diversas views e diversas threads
em paralelo ,
Uma conseqncia natural de termos mais espao disponvel na tela a utilizao
de vrias views menores para compor a tela e criar um layout diferenciado.
E para criar todas estas views, onde cada uma possui um diferente contedo,
algumas perguntas que ficam no ar so:
Como fazemos para fornecer o contedo para todas as views, se temos vrias
delas espalhadas pela tela, e cada uma com um diferente contedo?
Ser que temos que criar uma thread para cada view?
Mas como fazemos para controlar tantas threads e a atualizao de todas as
views, uma de forma independente da outra?
Como fica o cdigo da activity?
Isso traz uma complexidade para a activity, uma vez que agora ela deixa de
controlar uma nica thread para controlar vrias threads que podem estar execu
tando ao mesmo tempo, uma vez que para popular cada view recomendada
uma thread diferente, pois dessa forma o contedo de cada view atualizado ao
mesmo tempo e de forma independente das outras.
Se voc parar para pensar, esse um trabalhinho chato. No impossvel, mas
realmente ser necessrio queimar alguns neurnios para implementar algo que
fique bom e reutilizvel.
Esse foi um dos problemas que surgiram com os tablets, pois uma das con
seqncias de ter bastante espao disponvel na tela foi, naturalmente, essa tela
ficar cheia de views, e cada uma com um contedo diferente.
Mas agora a boa notcia! A partir do Android 3.0 foi criada a API de Fragments
para auxiliar nessa tarefa de criar uma tela cheia de informaes e views diferentes.
Um fragment permite desvincular a responsabilidade de gerenciar o conte
do da activity, de forma que a activity principal pode ficar bem simples. Cada
fragment representa uma view, e na verdade ele adicionado na tela da mesma
forma que uma view comum.
Podemos definir um fragment como uma view poderosa que capaz de
gerenciar o seu contedo sozinha e at mesmo abrir uma thread para buscar as
informaes na internet, se necessrio. Na verdade, um fragment acaba sendo
uma miniactivity, pois cada um possui uma view (por padro) e possui um ciclo
196 Google An droi d para T ablets
Cap tulo 6 Fragmen ts
197
de vida bem definido, como o de uma activity com os mtodos onCreate(), onPauseQ ,
onStopQ etc., e capaz de gerenciar seu contedo por conta prpria, alm de tratar
todos os eventos gerados pela sua view.
Dessa forma, a activity principal, ao invs de adicionar diversas views e ser
responsvel por gerenciar os seus contedos, pode utilizar no lugar dessas
views vrios fragments, que na prtica vo representar as mesmas views, mas
A activity final ento ficar simples, assim como a lgica de negcios em cada
fragment. Literalmente, ao utilizar fragments, vamos quebrar o cdigo em pedaos
menores e reutilizveis.
Outra vantagem de se utilizar fragments a possibilidade de reutilizar esse
componente em diversas telas.
Depois dessa introduo, vamos iniciar com a parte prtica, pois muitas vezes
um projeto de exemplo fala mais que mil palavras.
Neste tpico vamos criar um projeto bem simples para explicar os fragments.
Para iniciar este captulo vamos criar um projeto com os seguintes dados.
Nome do projeto: LivroAndroid-Cap06-F ragments
Verso: Android 3.x
P a c o t e : br.livroandroid.cap06.fragments
Criar activity classe =Main.java
Verso mnima do SDK: 11(Android 3.0 ou superior)
Depois de termos criado o projeto, vamos construir uma simples tela, conforme
podemos visualizar na figura 63.
Um fragment permite desvincular a responsabilidade de gerenciar o
contedo da activity, de forma qu a activity principal pode ficar bem
simples. Cada fragment representa uma view na tela, e na verdade
ele adicionado na tela da mesma forma que uma view comum. ,
eles vo automatizar toda a tarefa e deixar a activity tranqila.
Um fragment uma miniactivity que tem sua prpria view e
responsvel por controlar o seu contedo e tratar os eventos da tela.
6.4 Criando o projeto com Android 3.x
194 Google An droi d para T ablets
f ~ ]
r ....
Anncio aqui
: :v ;i
- - - - - - 'r'->
i
)
Figura 6.2 - Aplicativo para tablets com uma view de anncios.
Mas como vamos adicionar a view de anncios em vrias activities, surge o
problema de que cada activity precisa controlar o anncio, buscar e atualizar o
seu contedo e tambm tratar os seus eventos. Consequentemente, vamos ter que
duplicar o cdigo do anncio em cada activity E claro que podemos criar algum
mtodo que centralize esse controle, para diminuir a duplicao de cdigo, mas
mesmo assim alguma coisa ser duplicada.
Para exemplificar teremos uma activity mais ou menos assim:
public class Telal ex tends Activity implements O nClick Listener {
(JOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.anuncio);
View view = findViewBy Id(R.id.anuncio);
view.setO nClick Listener(this);
// Buscar o conte do do an ncio utiliz ando uma thread...
}
@ 0verride
public void onClick (View view) {
II Tratar o clique no an ncio aqui, e outros eventos tamb m
>
}
Essa primeira activity naturalmente possui vrias views, cada uma com sua
lgica. E ela est inserindo o tratamento para o evento gerado pelo anncio.
Agora, na prxima tela, em outra activity, digamos que temos outro layout e
com outra lgica tambm, naturalmente. Dessa forma, na classe Tela2 teremos que
duplicar o cdigo que vai tratar o evento do anncio e, consequentemente, esto
comeando os nossos problemas.
public class Tela2 ex tends Activity implements O nClick Listener {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.anuncio);
View view = findViewByld(R.id.anuncio);
view.setO nClick Listener(this);
// Buscar o conte do do an ncio utiliz ando uma thread...
}
@ 0verride
public void onClick (View view) {
// Tratar o cliq ue no an ncio aqui, e outros eventos tamb m
}
}
O anncio foi apenas um exemplo, mas em aplicaes para tablets comum
termos que reaproveitar as views em diversas telas e, portanto, acabamos dupli
cando o cdigo na activity E claro que existem formas de deixar isso organizado,
mas no esse o caso.
Outra coisa bastante comum em aplicativos para tablets uma tela que uti
liza pequenas views distribudas de forma elegante para ocupar todo o espao
disponvel, como se fosse um portal na web, com diversas janelas para ocupar
todo o espao do site.
Cada uma dessas views precisa buscar o seu contedo de algum lugar, e fica
complexo demais para a activity controlar tudo isso sozinha.
Para solucionar esses problemas, a partir do Android 3.0 foi criada a API de
F ragments que vamos estudar neste captulo.
Cap tulo 6 Fragmen ts ^ 195
98 Google An droi d para T ablets
Por padro, o ProgressBar exibe uma pequena animao automaticamente,
sem que seja preciso ativ-lo, conforme a figura 2.21.
Li vro Googl e
Androi d
aWd r o i d
Este livro dedicad^Hs
desenvolvedores I ndrcsd que desejam
aprimorar seus conucmentos e estudar
as novas funcionalidades presentes no
Android 3.x, como fragments e
actionbar.
Figura 2.21 - Tela de sobre com ProgressBar no W ebView.
A tela de detalhes exibe agora um ProgressBar enquanto o WebView est
carregando a pgina, mas o problema agora que ele fica l para sempre. Ento
devemos monitorar o carregamento da pgina do Web View para esconder o
ProgressBar no final.
Felizmente, isso simples de se fazer no Android. Basta utilizar uma imple
mentao customizada da classe android.webk it.W ebViewClient da seguinte forma:
private void monitoraW ebView(W ebView webview) {
webview.setW ebViewClient(new W ebViewClientQ {
(SOverride
public void onPageStarted(W ebView view, String uri, Bitmap favicon) {
super.onPageStarted(view, uri, favicon);
Log.i(TAG, " onPageStarted" );
ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
progress.setvisibility (View. VISIBLE);
}
gO verride
public void onPageF inished(W ebView view, String uri) {
super.onPageF inished(view, uri);
Log.i(TAG, " onPageF inished" );
ProgressBar progress = (ProgressBar) findViewByld(R .id.progress);
progress.setVisibility (View.INVISIBLE);
}
});
}
Utilizando um W ebViewClient podemos monitorar o incio e o fim do carrega
mento da pgina e, principalmente no fim, podemos esconder a animao do
ProgressBar facilmente. _
A seguir podemos visualizar o exemplo completo da tela de sobre,
f i a T elaSobre.java
pack age br.livroandroid.carros.activity ;
public class TelaSobre ex tends Activity {
private static final String TAG = "livroandroid" ;
private static final String U RL_ S0BRE = "http://www.livroandroid.com.br/livro/carros/sobre.htm";
(SOverride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.sobre);
W ebview webview = (W ebView) findViewBy ld(R.id.webview);
W ebSettings settings = webview.getSettings();
// Ativa o J avaScript na pgina
settings.setJ avaScriptEnabled(true);
// Publica a interface para o J avaScript
webview.addJ avascriptInterface(this, " LivroAndroid);
// Abre a pgina
webview. loadllrl (U RL_ SO BRE);
monitoraW ebView(webview);
}
private void monitoraW ebView(W ebView webview) {
webview.setW ebViewClient(new W ebViewClient() {
@ O verride
public void onPageStarted(W ebView view, String uri, Bitmap favicon) {
super.onPageStarted(view, uri, favicon);
Log.i(TAG, " onPageStarted");
}
gO verride
public void onPageF inished(W ebView view, String uri) {
super.onPageF inished(view, uri);
Log.i(TAG, "onPageF inished" );
ProgressBar progress = (ProgressBar) findViewByld(R.id.progress);
progress.setVisibility (View.INVISIBLE); *
}
Cap tulo 2 Apli cati vo de exemplo 99
&
100 Google An droi d para T ablets
// M todo chamado pela pgina html no W ebView
public void voltar{ ) {
Log.i(TA5, "voltar()");
// Ento encerramos a activity para demonstrar
finish();
}
}
2.21 Consideraes sobre o exemplo
Esse foi um exemplo simples, mas apresentou diversos conceitos importantes para
o desenvolvimento de aplicaes para o Android.
O exemplo abordou a busca de uma lista de carros na internet, o parser do
XML, a correta utilizao de threads e a atualizao de um ListView utilizando
o padro ViewH older. Vimos um interessante componente para fazer o download
das imagens na lista enquanto um ProgressBar exibido, e o resultado foi uma
aplicao que at que tem o seu valor.
Algumas explicaes foram corridas devido ao fato de este livro ser focado em
auxiliar desenvolvedores Android que j conhecem a plataforma. Dessa forma,
esse exemplo foi apresentado com o objetivo de servir como base para nossos
prximos captulos, onde iremos abordar alguns assuntos importantes, como, por
exemplo, a migrao passo a passo deste projeto para tablets.
Este projeto foi criado rapidamente com o objetivo de servir como
base para as explicaes dos prximos captulos, que vo fornecer
solues e boas prticas de desenvolvimento para Android, alm de
j apresentar novas APIs.
Ainda existe muito que melhorar nesse aplicativo, e apenas para ter uma ideia
de um dos problemas, faa o seguinte teste:
Depois de entrar no aplicativo e ter visualizado a lista de carros com a tela do
celular na vertical, tente posicionar o celular na horizontal e ver o que acontece.
Voc perceber que a lista de carros foi novamente carregada da internet, assim
como suas imagens (ou lidas do cache feito no carto de memria).
I sso pode no ser o desejado, uma vez que gerou mais trfego de rede, consu
mindo mais recursos, e at porque pode demorar novamente para carregar a tela.
Outra dvida seria: e se fosse necessrio criar um layout XML diferenciado
para quando o celular est na vertical ou na horizontal, como faramos?
As respostas para essas questes esto no prximo captulo. At logo!
CAPTULO 3
Controlando o estado de
sua activity
Uma activity representa uma tela da aplicao em que o usurio est, de alguma
forma, interagindo com o sistema. Entre outras responsabilidades, a activity precisa
controlar a interface da tela e seus eventos.
Como desenvolvedores, precisamos estar atentos ao ciclo de vida de uma
activity e sempre salvar o seu estado corretamente, para o caso de o processo da
aplicao vir a ser interrompido por outro aplicativo ou por alguma alterao nas
configuraes de sistema que vamos estudar neste captulo.
3.1 Vertical e horizontal
Smartphones modernos com o sistema operacional Android possuem o sensor
integrado e o usurio frequentemente gira o celular para a horizontal (deitado)
ou vertical (em p) para obter uma melhor visualizao do aplicativo.
Esse processo de girar o celular conhecido como troca de orientao. Para
seu desenvolvimento vamos utilizar a palavra portrait para a vertical e landscape
para a horizontal.
-Eor exemplo, o usurio pode estar com o celular na vertical e navegando no
Y ouTube procura de vdeos interessantes, e de repente ele escolhe um vdeo e
comea a assisti-lo. Usurios j acostumados aos recursos do Android iro rapi
damente posicionar o celular na horizontal para obter uma melhor visualizao
do vdeo e aproveitar melhor o espao na tela, uma vez que layouts diferentes so
exibidos para cada orientao.
101
102
Google An droi d para T ablets
Voc, como desenvolvedor, deve estar atento a essas caractersticas e projetar seu
aplicativo para responder a esses eventos de orientao e aproveitar ao mximo
os recursos que a plataforma tem a oferecer.
Como comportamento padro, o Android permite que as aplicaes funcionem
tanto na vertical quanto na horizontal, e o aplicativo com a lista de carros que
desenvolvemos anteriormente pode ser utilizado em ambas as orientaes, como
podemos visualizar nas figuras 3.1 e 3.2.
PorschePanamera
Lamborghini Aventador
Chevrolet Corvette206
BMWM5
-T* HB9:08
'
.*1
*
*
*CarrosShowcase: v.
Ferrari FF >
Renault MeganeRSTrophy
AUDIGTSpyder
PorschePanamera
Figuras 3.1 e 3.2 - Lista de carros na vertical e na horizontal.
O processo de girar o celular conhecido como troca de orientao.
Para seu desenvolvimento vamos utilizar a palavra portrait para' a
vertical e landscape para a horizontal.
3.2 Forando a tela a trabalhar na orientao desejada
Antes de comear este captulo recomendado que seja feita uma cpia do projeto
anterior LivroAndroid-Cap02-Carros, que pode ser renomeado como LivroAndroid-Cap03-
Carros, assim podemos customizar esse novo projeto e comparar com o anterior,
se necessrio.
As vezes necessrio que o aplicativo funcione somente na vertical ou na
horizontal, e a activity pode ser configurada para que funcione somente na
orientao desejada.
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
103
Na configurao da activity no AndroidManifest.xml podemos adicionar o pa
rmetro android:screenO rientation para forar a orientao.
Para a orientao vertical o valor que deve ser informado portrait (formato
retrato), e para a horizontal deve ser informado landscape (formato paisagem). Por
exemplo, para forar esse aplicativo a funcionar somente na vertical, poderamos
configurar cada activity da seguinte maneira:
< activity android:name^ M inhaActivity Aq ui" android:screenO rientation= " portrait" />
Portanto, vamos alterar o projeto dos carros desenvolvido anteriormente e
inserir a configurao android:screenO rientation= " portrait" na activity que exibe a
lista dos carros.
( An droi dMan i f est.xml
< ? xml version= 1.0" encoding= " utf-8" ? >
< manifest x mlns: android= " http://schemas. android. com/apk /res/android"
pack age= " br.livroandroid.carros
android:versionCode= " l"
android:versionName= " 1.0" >
< uses-sdk android:minSdk Version= " 4" />
< uses-permission android :name= "android.permission. INTERNET" />
< uses-permission android:name= " android.permission. ACCESS_ NETW O RK _ STATE" />
< uses-permission android:name= " android.permission.W RITE_ EX TERNAL_ STO RAGE" />
< application android :icon="(Sdrawable/icon" android:label= " @ string/app_ name"
android:theme= " @ sty le/tema" >
activity android:name= .Main" android:label= " @ string/app_ name" >
<intent-filter>
< action android:name= ''android.intent.action.HAIN" />
< category android:name= " android.intent.category .LA NCH ER" />
</intent-filter>
< /activity >
activity android:name= " .activity .TelaListaCarros" android:screenO rientation= " portrait" />
< activity android:name= " .activity .TelaDetalhesCarro" />
< activity android:name= " .activity .TelaSobre" />
</application>
< /manifest>
A figura 33 exibe o resultado desse exemplo. Agora, mesmo deitando o celular,
a lista de carros permanece sendo exibida na vertical, e o usurio forado a usar
o aplicativo na orientao configurada.
104
Google An droi d para T ablets
Figura 3.3 - Aplicativo somente na vertical. Se o celular for deitado, a tela no muda.
Muitas vezes a lgica de negcios pode variar conforme a orientao da tela,
ento sempre podemos descobrir qual a orientao atual pela API e at forar a
orientao da tela, se necessrio.
Para isso vamos criar a classe O rientacaoU tils com alguns mtodos utilitrios. Essa
classe uma boa candidata a entrar no projeto LivroAndroid-AndroidU tils e turbinar
cada vez mais o nosso projeto biblioteca.
# i Ori en tacaoUti ls.java
public class O rientacaoU tils {
// Verifica qual a orientao da tela
public static int getO rientacao(Contex t contex t) {
int orientacao = contex t.getResources().getConfiguration().orientation;
return orientacao;
}
// Verifica se a orientao da tela est na vertical
public static boolean isVertical(Contex t context) {
int orientacao = contex t,getResources().getConfiguration().orientation;
boolean vertical = orientacao == Configuraiion.0RIENTATI0N_P0RTRAIT;
return vertical;
}
// Verifica se a orientao da tela est na horiz ontal
public static boolean isH oriz ontal(Contex t context) {
int orientacao = contex t.getResources().getConfiguration().orientation;
boolean horiz ontal = orientacao == Configuration.0RIENTATI0N_LANDSCAPE;
return horizontal;
}
// Solicita a ex ecuo da activity na vertical
public static void setO rientacaoVertical(Activity contex t) {
contex t.setReq uestedO rientation(Activity lnfo. SCREEN_ O RIENTATIO N_ PO RTRAIT);
} I
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
105
// Solicita a ex ecuo a activity na horizontal
public static void setO rientacaoH oriz ontal(Activity context) {
contex t.setReq uestedO rientation(Activity lnfo.SCREEN O RIENTATIO N LANDSCAPE);
} "
}
Note que nessa classe existem mtodos para solicitar que a orientao da
activity seja forada para a vertical ou a horizontal, seguindo o mesmo princpio
da configurao que fizemos no AndroidManifest.xml.
Mas os mtodos mais importantes so os que descobrem se a tela est na
vertical ou na horizontal, porque muitas vezes a lgica de negcios pode mudar
conforme a orientao da tela. Ento fica aqui a seguinte dica de classe:
possvel forar a orientao da tela pela API utilizando o mtodo
Activity.setReq uestedO rientation(vertical ou horizontal). Mas
recomendado que estas configuraes estejam declaradas no
arquivo AndroidManifest:xml sempre que possvel para facilitar a
manuteno do cdigo.
3.3 Criando telas diferentes para a vertical e a horizontal
At aqui vimos o comportamento padro do Android e como ele automatica
mente cuida da orientao da tela, redimensionando e ajustando com perfeio
os componentes.
Mas como fazemos se for necessrio exibir uma tela com layouts diferentes
para a tela na vertical ou na horizontal?
Para a sorte dos desenvolvedores Android, isso extremamente simples de se
implementar.
Sempre criamos os layouts na pasta hes/layout, correto? Essa a pasta padro
dos arquivos de layout. Agora, se voc deseja criar layouts que funcionem espe
cificamente na horizontal, podemos utilizar a pasta /res/layout-land. Dessa forma
podemos ter dois arquivos diferentes, mas com o mesmo nome, como, por exemplo,
/res/layout/carros.xml e /res/layout-land/carros.xml. O resto voc deixa com o Android
e ele ir escolher o layout correto conforme a orientao da tela.
Para exemplificar vamos criar layouts distintos, conforme podemos visualizar
nas figuras 3.4 e 35.
106
Google An droi d para T ablets
)
)
)
AUDIGTSpyder ^
O mais novo modelo limitado a 333 unidades que vem para preench...
O Porsche Panamera um modelo coup de 4 portas de porte grand-.
Lamborghini Aventador y
Outro modelo que estreou em Genebra, conta com um desempenho...
Figuras 3.4 e 3.5 - Tela de carros diferente para a vertical e a horizontal.
Note que na vertical somente vamos exibir o nome do carro, enquanto que na
horizontal tambm ser exibida a descrio.
E como temos mais espao disponvel na tela se o aparelho est na vertical,
sobra espao para colocar alguma informao no rodap da pgina.
Ento mos obra e comecemos a customizao das telas. Primeiramente
vamos criar a pasta /res/layout-land e duplicar os arquivos carros.xml e carrojtem.
xml. Para comear voc deve ter a seguinte estrutura de diretrios com esses dois
arquivos duplicados:
/res/layout/carros.xml
/res/layout/carro Jtem.xml
/res/layout-land!'carros.xml
/res/layout-land/carro_item.xml

Nem sempre a orientao vertical a padro. Nos novos tablets


geralmente a orientao padro horizontal, sendo que diversos
aplicativos em tablets ficam mais bem visualizados dessa forma.
Nesse caso, se fosse necessrio garantir que algum recurso fosse
disponibilizado apnas para a vertical, poderamos utilizar a pasta /
res/layout-port.
J
Lembre que o arquivo /res/layout/carros.xml exibe o rodap da pgina na vertical,
e isso feito com o include do arquivo /res/layout/include_Jooter.xml dessa forma.
< include lay out= " @ lay out/include_ footer" />
Vamos continuar com a customizao e criar o layout da lista de carros para
a horizontal. A nica diferena que vamos omitir a declarao desse include, e
o layout ficar da seguinte forma:
/res/layout-lan d/carros.xml
< ? xml version= " 1.0" encoding=' 'utf-8"?>
LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android
android:orientation= " vertical"
android: layout_width="fill_parent"
android: layout_ height="fill_parent"
android:gravity = center
android: back ground='' @color/fundo" >
cinclude lay out= " @ lay out/include_ header" />
< ListView
android:id= " @ + id/listview"
android:layout_ width= " fill_ parent
android:lay out_ height= " 0dp"
android:lay out_ weight= " l
android: cacheColorH int= " @ color/transparente' />
<!-- No faz emos o include do rodap aq ui -->
< /LinearLay out>
Pronto, agora o rodap vai aparecer apenas na vertical.
Essa uma maneira de implementar essa funcionalidade. Outra seria deixar
o arquivo /res/layout/carros.xml intacto, sem a necessidade de criar uma cpia para
a horizontal. Nesse caso poderamos alterar somente o arquivo de rodap, para
que na sua verso na horizontal ele simplesmente, no exibisse nada.
Poderamos tambm ter optado por criar o arquivo /res/layout-land/indude Jooter.
xml especfico para a horizontal da seguinte forma:
/res/layout-lan d/i n clude_f ooter.xml
<? xml version= " 1.0encoding= " utf-8" ? >
<LinearLay out x mlns: android= " http: //schemas. android. com/apk /res/android"
android:lay out_ width= " wrap_ content"
android:layout_ height='' 0dp" >
<!-- Vaz io -->
< /LinearLay out>
Cap tulo 3 Con trolan do o estado de sua Acti vi ty ^ 107
Dessa forma o arquivo de rodap seria vazio na horizontal e no ocuparia
espao nenhum. Note que a altura foi declarada como zero com o atributo
android:lay out_ height= " 0dp" .
Temos ainda outra maneira de implementar a mesma funcionalidade, que seria
utilizando a API diretamente no cdigo para esconder o layout se a orientao for
horizontal. Para isso seria necessrio atribuir um identificador ao layout do rodap,
como, por exemplo, android:id= " + id/lay outF ooter" , e utilizar um cdigo como este:
boolean horiz ontal = O rientacaoU tils.isH oriz ontal(this);
if (horiz ontal) {
LinearLay out lay outF ooter = (LinearLay out) findViewBy ld(R.id.layoutF ooter)j
if (lay outF ooter != null) {
lay outF ooter.setVisibility (View.GO NE);
}
}
Agora com voc! J mostramos trs maneiras de deixar o rodap visvel apenas
na vertical. Cabe a voc escolher a forma que mais lhe agrada.
Vamos continuar o nosso exemplo, e o prximo passo adicionar a descrio
do carro somente na horizontal.
Para isso vamos duplicar o arquivo /res/layoutlcarro_item.xml para Iresllayout-land/
carro_item.xml. Apenas para lembrar, esse layout utilizado pela classe CarroAdapter
para customizar o layout de cada linha onde um carro exibido.
Apenas para facilitar a explicao deste exemplo, o cdigo-fonte a seguir est
exibindo somente a parte do arquivo em que foi adicionada a descrio do carro.
O restando do cdigo continua exatamente igual ao que estava na tela na vertical.
A nica diferena para o arquivo da vertical que neste foi adicionado um Tex tView
para a descrio do carro, com a chave android:id= " @ + id/tDesc" .
& /res/layout-lan d/carro_i tem.xml
< LinearLay out
android:lay out_ width= " wrap_ content"
android: lay out_ height=' 'wrap_ content"
android:lay out_ marginRight= " 5dp"
android:orientation= " verticar
android: layout_marginLeft="6dp' '
>
<!-- Cdigo simplificado aqui -->
<!-- Nome do carro -->
108 Google An droi d para T ablets
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
109
< Tex tView
android:id= " @ + id/tNome"
android: lay out_width= "fillj )arent"
android:lay out_ height= wrap_ content
android:tex tColor= " @ color/preto"
android:tex t= " @ string/nome
sty le= " @ sty le/tex tol4Negrito
/>
<!-- Descrio -->
< Tex tView
android:id= g+ id/tDesc"
android: layout_ width= "fill_ parent"
android:lay out_ height= wrap_ content"
android:tex t= " @ string/descricao"
sty le= " @ sty le/tex tol2
android:max Length= " 100"
android:ellipsiz e= " end"
android:singleLine= " true"
android:lay out_ marginRight= " 16dp"
/>
< ! Cdigo simplificado aq ui -->
< /LinearLay out>
Depois de alterar o arquivo de layout para exibir a descrio do carro na hori
zontal, precisamos alterar tambm a classe CarroAdapter para recuperar esseTextView
e, caso seja encontrado, atualizar o seu valor com a descrio do carro. Note que
o TextView foi configurado no XML com as propriedades androidimax Length^ ieee
android:ellipsiz e= " endpara limitar o seu contedo.
A seguir podemos visualizar a classe CarroAdapter atualizada.
[& CarroAdapter.java
public dass CarroAdapter ex tends BaseAdapter {
protected static final String TAG = " livroandroid";
private Layoutlnflater infater;
private final List< Carro> carrosj
private final Activity context;
private DownloadlmagemU til downloader;
public CarroAdapter(Activity context, List< Carro> carros) {
this. contex t = contex t;
this. carros = carros;
this.inflater = (Layoutlnflater) contex t.getSy stemService(Contex t.LAY O U T_ INF LATER_ SERVICE);
i
// U tiliz a este obj eto para recuperar a classe q ue faz o download de imagens
downloader = new DownloadlmagemU til(contex t);
}
gO verride
public int getCount() {
return carros != null ? carros.siz eQ : 0;
}
gO verride
public O bj ect getltem(int position) {
return carros != null ? carros.get(position) : null;
}
@ O verride
public long getltemld(int position) {
return position;
}
@ O verride
public View getView(int position, View view, ViewGroup parent) {
ViewH older holder = null;
if (view null) {
// No ex iste a View no cache para esta linha, ento criar um novo
holder = new ViewH olderQ ;
// Busca o lay out para cada carro com a foto
int lay out = R.lay out.carro_ item;
view = inflater.inf!ate(layout, null);
view.setTag(holder);
holder.tNome = (TextView) view.findViewByld(R.id.tNome);
holder.tDesc = (TextView) view.fndViewById(R.id.tDesc);
holder.imgF oto = (ImageView) view.findViewByld(R.id.img);
holder.progress = (ProgressBar) view.findViewByld(R.id.progress);
} else {
// J ex iste no cach. Bingo, ento pega!
holder = (ViewHolder) view.getTagQ ;
}
holder. imgF oto.setlmageBitmap(null);
Carro c = carros.get(position);
// Agora q ue temos a view atualiz ada os valores
holder.tNome.setTex t(c.nome);
if (holder.tDesc != null) {
holder.tDesc.setTex t(c. desc);
}
downloader.download(contex t, c.urlF oto, holder.imgF oto, holder.progress);
return view;
110 Google An droi d para T ablets
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
111
// Design Patter "ViewH older" para Android
static class ViewH older {
Tex tView tNome;
Tex tView tDesc;
ImageView imgFoto;
ProgressBar progress;
}
}
Pronto! A customizao est feita, e agora temos telas diferentes para a vertical
e a horizontal. No foi assim to difcil, no ? Se voc seguiu o exemplo correta
mente ao executar o projeto, o resultado deve ter ficado como as figuras 3.4 e 3.5,
exibidas o incio deste tpico.
Podemos utilizar as pastas /res/layout-port (portrait) para vertical
e /res/layout-land (landscape) para a horizontal. Em smartphones
a pasta / res/layout representa a /res/layout-port, uma vez que a
orientao padro vertical. J nos novos tablets de 10 com Android
3.x ou superior, a orientao padro a horizontal, portanto tome
cuidado. Nesse caso a pasta /res/layout corresponde a /res/layout-
land. Portanto, se voc tem conhecimento de que seu aplicativo vai
funcionar em smartphones e tablets ao mesmo tempo, no assuma
nunca que a orientao padro seja a vertical.
3.4 0 problema: o Android destri e recria a activity ao trocar de
orientao
Para comear este tpico vou pedir a voc para realizar um teste simples. Com
o celular na vertical, entre na tela de lista de carros e depois gire a tela para a
horizontal. Feito isso, o que aconteceu?
Creio que voc pode perceber que a tela foi recriada, pois a mensagem de
aguarde apareceu na tela, e a lista de carros foi carregada novamente da internet.
Se voc est em uma rede Wi-Fi, isso talvez tenha sido bem rpido, mas caso esteja
utilizando a conexo de dados da operadora e o sinal no esteja muito bom, o
comportamento da aplicao pode ter sido extremamente ruim, e com certeza
o usurio no vai ficar satisfeito por ter de esperar, nem que seja por poucos se
gundos, para a aplicao recarregar a lista de carros.
Tambm comum no desenvolvimento de aplicativos Android criarmos layouts
diferenciados para a vertical e a horizontal ou simplesmente permitir que o mesmo
layout seja utilizado em ambas as orientaes.
Quando criamos um layout diferenciado para a horizontal, a princpio achamos
que tudo funcionar perfeitamente, mas quando deitamos o celular, Boom!, a
aplicao trava inesperadamente e o processo encerrado. Isso no nada bom,
mas se j aconteceu com voc, fique tranqilo, porque agora vamos entender o
motivo e verificar alguns detalhes, alm de dar dicas importantes.
Mas, afinal, o que acontece quando giramos a tela do aparelho?
Para trocar o layout da tela, o Android vai destruir a activity atual e criar uma
totalmente nova utilizando o novo layout. Se inserirmos alguns logs nos mtodos
onCreate() e onDestroy O , poderemos verificar claramente isso acontecendo.
Vamos numerar de forma resumida a seqncia dos eventos durante o ciclo
de vida de uma activity, quando ocorre a troca de orientao, apenas para exem
plificar como isso funciona.
1. Activity criada, e o mtodo onCreate() chamado.
2. Tudo funciona perfeitamente com o layout na vertical /res/layout. A lista de
carros carregada da internet.
3. O usurio posiciona o celular na horizontal.
4. O Android vai destruir a activity atual, chamando o mtodo onDestroy () para
terminar o seu ciclo de vida.
5. O Android cria uma nova activity, e o mtodo onCreateQ chamado nova
mente. Nesse momento temos uma nova instncia de Activity , e todos os
dados anteriores foram perdidos. Em nosso projeto de exemplo perdemos
toda a lista de carros.
6. Agora, com o celular na horizontal, automaticamente o layout desejado
utilizado com o usa da pasta /res/layout-land.
7. O fluxo continua do zero, e a lista de carros novamente carregada da
internet.
Por isso muitas vezes algumas aplicaes travam ao serem colocadas na ho
rizontal, pois elas perdem-o seu estado, e os objetos so destrudos. O que voc
tem depois disso uma activity zerada, que acabou de ser criada.
No nosso aplicativo, o que acontece que a lista de carros carregada nova
mente na internet, uma vez que a activity perde o seu estado, e isso definitivamente
no recomendado.
112 Google An droi d para T ablets
Cap tula 3 Con trolan do o estado de sua Acti vi ty
113
Dessa forma temos trs opes para preservar o estado de uma activity:
L Buscar'as informaes novamente no mtodo onCreate(). Ok, eu sei que no
era bem isso que voc estava pensando em ler, ento vamos direto para a
prxima sugesto. Afinal, isso o aplicativo j est fazendo, correto?
2. Salvar as informaes em um mtodo que faz parte do ciclo de vida do
Android e restaur-las quando a nova activity for criada. Existe alguns
mtodos utilizados para isso, como o onSavelnstanceState(bundle) e o onRetainN
onConfigurationInstance().
3. I nformar ao Android que no troque a orientao da tela automaticamen
te, e voc, como um desenvolvedor experiente, vai se encarregar de tudo.
Dessa forma a activity no ser destruda, e voc ter que redesenhar a tela
manualmente.
Nos prximos tpicos vamos verificar como tratar essas situaes e dar um
acabamento mais profissional ao projeto.
3.5 Salvando o estado da tela - onSavelnstanceState()
Atualmente nosso projeto busca os carros na internet e exibe corretamente suas
informaes na tela. Mas se trocarmos a orientao da tela, a activity atual ser
destruda para que se crie uma nova, e toda a lista de carros ser carregada no
vamente, deixando a troca de orientao um pouco lenta.
Para solucionar esse problema precisamos salvar o estado da tela antes de o
Android destruir a activity
Para isso vamos utilizar o mtodo onSavelnstanceState(bundle), que chamado
pelo Android sempre que este est pensando em destruir a activity atual. Isso
pode acontecer durante a troca de orientao da tela ou at mesmo em casos em
que o Android decide matar uma aplicao que est em segundo plano e no
est sendo utilizada. Nesses casos, quando o Android decide destruir a activity,
esse mtodo sempre ser chamado.
Note que ao chamar o mtodo finish() explicitamente no cdigo,
estamos conscientemente fechando a activity, e desta forma o
Android no chamar o mtodo onSavelnstanceState(bundle).
Para corrigir o exemplo anteri or precisamos impl ementar o mtodo
onSavelnstanceState(bundle) na classe TelaListaCarros para salvar a lista de carros da
qual fizemos download, ou qualquer outro objeto necessrio.
)
114
Google An droi d para T ablets
@ O verride
protected void onSaveInstanceState(Bundle outstate) {
super.onSavelnstanceState(outState);
// Salvar os carros aqui
) >
A classe Bundle aceita como parmetros strings, tipos primitivos, array de bytes,
a interface Serializ able ou a interface Parcelable do Android. A maneira mais simples
de salvar toda a lista de-earros seria utilizando a interface Serializ able, devido a sua
facilidade de implementao. Mas o problema que precisamos salvar uma lista
inteira de carros, e no existe mtodo para isso. Existe apenas um mtodo para
salvar um nico Serializ able, e no uma lista inteira como um List< Serializ able> .
Para contornar o problema vamos criar uma classe auxiliar chamada ListaCarros
, que vai conter a lista de carros e implementar a interface Serializ able para que
esse objeto possa ser salvo no Bundle.
i Li staCarros.java
public class ListaCarros implements Serializ able {
private static final long serialVersionJ ID = -2251881666082662021L;
public static final String K EV = carros";
public List< Carro> carros;
public ListaCarros(List< Carro> carros) {
i this.carros = carros;
Pronto! O nosso artifcio tcnico j est criado, e agora podemos salvar esse
objeto ListaCarros da seguinte forma.
(SOverride
protected void onSaveInstanceState(Bundle outstate) {
super.onSavelnstanceState(outState);
// Salvar o estado da tela
outstate.putSerializ able(ListaCarros.KEY, new ListaCarros(carros));
}
O objeto Bundle que recebemos como parmetro utilizado para salvar os ob
jetos necessrios, e nesse caso salvamos um objeto que contm a lista de carros.
Posteriormente esse mesmo Bundle ser passado nova activity quando ela for
criada. Para isso a assinatura do mtodo onCreate(bundle) recebe esse objeto, con
forme podemos verificar no seguinte trecho de cdigo da classe TelaListaCarros:
Cap tulo 3 Con trolan do o estado de sua Acti vi ty 115
(
public class TelaListaCarros extends LivroAndroidActivity implements OnltemClickListener, Transacao {
private static final String TAG = " livroandroid";
private ListView listView;
private List< Carro> carros;
private String tipo;
(Sverride
public void onCreate(Bundle savedlnstanceState) {
Na primeira vez que uma activity criada, esse Bundle recebido como parmetro
no mtodo onCreate(bundle) nulo. Mas se o estado da tela foi salvo com o mtodo
onSaveInstanceState( bundle), aquele mesmo Bundle que foi salvo agora passado para
a nova activity
Portanto, podemos testar se o Bundle no nulo para descobrir se a activity est
sendo restaurada e, nesse caso, recuperar a lista de carros que salvamos. Dessa
forma podemos utilizar um mtodo mais ou menos assim:
if (savedlnstanceState != null) {
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
this.carros = lista.carros;
}
if (carros != null) {
// Atualiz a o ListView com a lista de carros q ue foi salva
} else {
// Busca os carros...
}
A seguir podemos verificar o cdigo-fonte completo desse exemplo, que est
salvando o estado da activity ao trocar a orientao da tela.
T elaLi staCarros.java
public class TelaListaCarros extends LivroAndroidActivity implements O nltemClick Listener, Transacao {
private ListView listView;
private List< Carro> carros;
private String tipo;
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros); .
tipo = getIntent().getStringEx traftipo" );
listView = (ListView) findViewById(R.id.listview);
listView.setO nltemClick Listener(this);
if (savedlnstanceState != null) {
116
Google An droi d para T ablets
// Recuperamos a lista de carros salva pelo onSavelnstanceState(bundle)
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
this.carros = lista.carros;
}
if (carros != null) {
// Atualiz a o ListView diretamente
listView.setAdapter(new CarroAdapter(this, carros));
} else {
startTransacao(this);
}
}
(SOverride
protected void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
// Salvar o estado da tela
outState.putSerializ able(ListaCarros.K EY , new ListaCarros(carros))j
}
@ 0verride
public void ex ecutar() throws Ex ception {
// Busca os carros em uma thread
this.carros = CarroService.getCarros(this, tipo);
}
SO verride
public void atualiz arViewQ {
// Atualiz a os carros na thread principal
if (carros != null) {
listView.setAdapter(new CarroAdapter(this, carros));
}
}
(SOverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapterQ .getltem(posicao);
Intent intent = new Intent(this, TelaDetalhesCarro.dass);
intent.putEx tra(Carro.K EY , c);
startActivity (intent);
}
}
Alternativamente podemos implementar o mtodo onRestorelnstanceState(bundle)
para ler o estado da activity Esse mtodo chamado sempre que ela est sendo
restaurada e sempre depois do mtodo onStartQ . Geralmente podemos utilizar
somente o mtodo onCreate(bundle) para recuperar o estado, mas se for necessrio
ler os objetos salvos somente depois do mtodo onStart(), este pode ser utilizado.
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
117
gO verride
protected void onRestoreInstanceState(Bundle savedlnstanceState) {
super. onRestorelnstanceState(savedlnstanceState);
// Colocar seu cdigo de restaurao do estado aqui
}
-v'? - -w ' ri . , r.
importante lembrar que o Android salva automaticamente o estado
das views, como, por exemplo, textos digitados em formulrios. om
isso voc no precisa se preocupar, pois nada digitado ser perdido
ao girar o aparelho. Mas nesse projeto tenos tuna lista de objetos
que foi carregada dinamicamente da web, nesse^caso e necessrio
salvar as informaes manualmente.'" i * "
3.6 Salvando o estado da tela - onRetainNonConfigurationlnstanceO
Neste tpico vamos abordar outra maneira de salvar o estado de uma activity.
Sempre que um evento relacionado a alguma alterao na configurao da aplica
o ou de sistema ocorrer, o Android ir destruir a activity e j criar esta em seguida.
Esses eventos podem ser a troca de orientao da tela (vertical/horizontal) ou a
abertura e fechamento do teclado fsico, por exemplo. Este ltimo em smartphones
que possuem o teclado, claro.
Sempre que ocorrer algum desses eventos, o mtodo onRetainNonConfigurationlns-
t a n c e Q ser chamado para dar a chance de o desenvolvedor salvar o estado da
tela. Esse mtodo precisa retornar um O bj ect, de forma que seja possvel retornar
qualquer objeto que contenha as informaes na tela. Posteriormente, quando a
activity for recriada, ser possvel recuperar esse objeto salvo utilizando o mtodo
getLastNonConfigurationlnstance(). A vantagem dessa abordagem que possvel salvar
qualquer objeto de forma simples e sem a necessidade de fazer a serializao.
O mtodo onRetainNonConfigurationInstance() chamado especificamente quando
ocorrem trocas de configuraes de sistema, como a troca de orientao (verti
cal/horizontal), a abertura do teclado fsico em celulares com teclado, a troca do
idioma do aparelho, entre outras. Neste caso estamos abordando a alterao de
orientao, pois uma das mais comuns.
Em nosso projeto podemos utilizar esse mtodo para salvar a lista de carros
da qual fizemos o download. E posteriormente, quando o Android recriar a tela,
podemos chamar o mtodo getLastNonConfigurationInstance() para recuperar a lista
que salvamos. A implementao desse mtodo simples, e a seguir exibido o
trecho principal que precisaria ser alterado no exemplo: I
118
Google An droi d para T ablets
@ O verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
listview = (Listview) findViewById( R. id. listview);
listview.setO nltemClick Listener(this);
// Recupera o obj eto salvo previamente
this.carros = (List< Carro> ) getLastNonConfigurationInstance();
if (carros != null) {
// Atualiz a o Listview
listview.setAdapter(new CarroAdapter(this, carros));
} else {
// Busca novamente os carros...
startTransacao(this);
}
}
Override
public O bj ect onRetainNonConfigurationlnstanceQ {
// Basta retornar o obj eto que precisa ser salvo aqui
return carros;
}
3.7 Salvando o estado da tela - Qual mtodo utilizar?
Conforme vimos, existem duas maneiras de salvar o estado da tela:
1. Utilizar o mtodo onSavelnstanceState(bundle) para salvar os objetos uti
lizando um Bundle e recuperar o estado no mtodo onCreate(bundle) ou
onRestorelnstanceState(bundle).
2. Utilizar o mtodo onRetainNonConfigurationInstance() para retornar um objeto e
recuper-lo utilizando o mtodo getLastNonConfigurationlnstance().
Mas e agora? Qual das maneiras devemos utilizar?
Conforme a documentao do Android, o mtodo onSaveinstanceState(bundle)
chamado sempre que o Android vai destruir a activity por qualquer motivo (ou
talvez v), e talvez at porque o sistema operacional esteja precisando liberar re
cursos e memria, ou est recebendo uma ligao telefnica, por exemplo. Dessa
forma, ao salvar o estado no h certeza de que a activity terha sido destruda
devido a uma troca da orientao do celular.
Cap tulo 3 Con trolan do o estado de sua Acti vi ty 119
Por exemplo, digamos que o usurio est utilizando a sua aplicao, mas de
repente ele recebe um email e abre a notificao para visualiz-lo. Quando o apli
cativo do Gmail aberto para a visualizao do email recebido, a sua aplicao
ficar em segundo plano. Agora vamos dizer que depois de ler o email o usurio
lembra que precisa fazer uma ligao para algum amigo. Ento ele prossegue, faz
essa ligao e, depois que termina, coloca o celular no bolso e vai tomar um caf.
Neste caso, infelizmente o usurio literalmente esqueceu que sua aplicao ficou
l executando, ou melhor, parada em segundo plano.
Nesses casos o Android pode vir a matar o processo da aplicao, pois esta
no est mais sendo utilizada. Mas o que vai acontecer com a lista de carros da
qual fizemos o download?
Esse o objetivo do mtodo onSavelnstanceState(bundle) e por isso ele faz parte
do ciclo de vida de uma activity Ele chamado quando o Android vai matar o
processo, mas no sabe ao certo quando a aplicao vai ser utilizada novamente,
e os dados precisam ser salvos de alguma forma. Ento, se salvarmos a lista de
carros utilizando esse mtodo hoje, podemos abrir a nossa aplicao de carros
somente amanh quando a lista estar l salva e pronta para ser lida a partir
do bundle que fornecido juntamente com o mtodo onCreate(bundle).
J o mtodo onRetainNonConfigurationInstance() chamado explicitamente quando
alguma configurao de sistema alterada, como, por exemplo, no caso de posicio
namento do celular na horizontal ou de abertura do teclado fsico. Esse mtodo s
chamado pelo Android quando ele tem certeza de que ir destruir uma activity
e logo em seguida recri-la. Dessa forma voc tem certeza de que depois de este
mtodo ser chamado, o mtodo onDestroy Q ser chamado logo em seguida para
destru-la, e na seqncia uma nova activity ser criada instantaneamente, quando
o mtodo onCreate(bundle) ser chamado. Mas dessa vez voc utilizar o mtodo
getLastNonConfigurationInstance() para ler o objeto salvo anteriormente.
Segundo a documentao, O mtodo onRetainNonConfigurationInstance() mais rpi
do que o onSavelnstanceState(bundle), pois no requer a serializao das informaes
para salvar os dados. Por outro lado, o mtodo onSavelnstanceState(bundle) sempre
chamado, seja em casos como este em que o celular posicionado na horizontal
ou quando uma activity est sendo destruda pelo sistema para liberar recursos.
Portanto, recomendado implementar os dois mtodos, ou pelo menos o
onSavelnstanceState(bundle). I mplementando o mtodo onSavelnstanceState(bundle) te
mos a garantia de que sempre salvaremos o estado da activity, e ao implementar
o mtodo onRetainNonConfigurationlnstanceO podemos estar focando na performance.
120
Google An droi d para T ablets
r'33'' VSFaa b seguinte teste para validar esses dois mtodos. Primeiramente'
A: troque a orientado do celular e note que ambos os mtodos sero:
p - chamados. Depois pressione a tecla HOME do aparelho para voltar.
wP - 'praa tela inicial do Android. Nesse ltimo caso, somente o'mtodo'
: ;M o n S a v n s t a n c e S t a t e ( b u n d l e ) ' ser c h a m a d a '% ; : | /Jj- - '
A seguir podemos verificar a implementao final com ambos os mtodos.
Note que foram adicionados alguns logs, que vamos explicar a seguir.
i a T elaLi staCarros.java
public class TelaListaCarros ex tends LivroAndroidActivity implements O nltemClick Listener, Transacao {
private static final String TA6 = " livroandroid" ;
private Listview listview;
private List< Carro> carros;
private String tipo;
@ SuppressW arnings(" uncheck ed" )
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
tipo = getIntent().getStringEx tra(" tipo" );
listview = (Listview) findViewByld(R.id.listview);
listview.setO nltemClick Listener(this);
carros = (List< Carro> ) getLastNonConfigurationInstance();
Log.i(TAGj " Lendo estado: getLastNonConfigurationInstance()" );
if (carros == null && savedlnstanceState != null) {
// Recuperamos a lista de carros salva pelo onSavelnstanceState(bundle)
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
Log.i(TAG," Lendo estado: savedlnstanceState(carros));
this.carros = lista.carros;
}
if (carros != null) {
// Atualiz a o Listview diretamente
listview.setAdapter(new CarroAdapter(this, carros));
} else {
startTransacao(this);
}
}
@ verride
public O bj ect onRetainNonConfigurationlnstanceO {
Log.i(TAG, " Salvando Estado: onRetainNonConfigurationlnstanceQ " );
return carros;
Cap tulo 3 Con trolan do o estado de sua Acti vi ty 121
gO verride
protected void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState); '
Log.i(TAGj Salvando Estado: onSavelnstanceState(bundle)" );
// Salvar o estado da tela
outState.putSerializ able(ListaCarros.K EY , new ListaCarros(carros));
}
gO verride
public void ex ecutarQ throws Ex ception {
// Busca os carros em uma thread
this.carros = CarroService.getCarrosthis, tipo);
}
gO verride
public void atualiz arView() {
// Atualiz a os carros na thread principal
if (carros != null) {
listView.setAdapter(new CarroAdapter(this, carros));
}
}
gO verride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapter().getItem(posicao);
Intent intent = new Intent(this, TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , c);
startActivity (intent);
}
}
Agora vamos executar a aplicao primeiramente na vertical. Feito isso, ao
posicionar o aparelho na horizontal, os seguintes logs aparecero no LogCat.
INF O /livroandroid: Salvando Estado: onSavelnstanceState(bundle)
INF O /livroandroid: Salvando Estado: onRetainNonConfigurationInstance()
INFO /livroandroid: Lendo estado: getLastNonConfigurationlnstanceQ
Portanto, nesse caso, como fizemos uma alterao na orientao do aparelho,
os mtodos onRetainNonConfigupationlnstanceQ e getLastNonConfigurationInstance() foram
suficientes para recuperar o estado da aplicao.
Agora vamos fazer mais um teste. Desta vez pressione a tecla H O M E. Ao fazer isso
um nico log exibido.
INF O/livroandroid: Salvando Estado: onSavelnstanceState(bundle)
O Android fez isso porque no sabe se voc vai ou no voltar para a sua acti
vity Agora vgmos fazer um teste radical e matar o processo de nossa aplicao.
Podemos fazer isso clicando no boto STOP dentro da janela Devices no Eclipse
ou utilizando qualquer aplicativo que mate processos no Android, como os fa
mosos " Task K iller" . Depois de matar o processo da aplicao, vamos reinici-la
selecionando-a na tela inicial H O M E do Android. Desta vez o que ocorre que pode
mos ver dois mtodos sendo chamados. Isso porque o primeiro mtodo retornou
um objeto nulo, mas o segundo mtodo salvou a ptria e tinha a lista de carros
previamente carregada. Essa a vantagem do mtodo onSavelnstanceState(bundle),
uma vez que ele sempre chamado.
INF O /livroandroid: Lendo estado: getLastNonConfigurationlnstanceQ
INF O /livroandroid: Lendo estado: savedlnstanceState(carros)
Agora com voc! Utilize esses mtodos com sabedoria para proporcionar ao
usurio uma navegao rpida, evitando buscas desnecessrias na web.
3.8 Fazer cache das imagens para melhorar a performance
Nesse aplicativo estamos utilizando aquele componente mgico que faz o down
load das imagens. Mas se voc reparar, ao girar o celular para trocar a orientao,
as imagens parecem ser carregadas novamente, pois o ProgressBar aparece por
alguns milissegundos.
Na verdade, o que acontece que as imagens esto salvas no carto de memria
e so lidas a partir dele. Embora ler cada imagem do carto de memria seja mais
rpido do que busc-las uma por uma na internet, mesmo assim temos o efeito
de o progresso aparecer por poucos milissegundos.
Apenas para relembrar, a seguir podemos verificar a classe Carro e podemos
verificar que ela contm a URL da foto. Se cada carro tivesse um atributo para
salvar um array de bytes da foto, por exemplo, no seria necessrio ler a imagem
novamente. Mas neste caso, como estamos deixando nessa classe apenas a URL
da foto, a classe utilitria DownloadlmagemU til realiza o trabalho de fazer o download
dessa imagem e salv-la no carto de memria para futuramente utilizar apenas
esse arquivo local e aproveitar o cache.
b Carro.java
public class Carro implements Serializ able {
private static final long serialVersionU ID = 6601006766832473959L;
public static final String K EY = "carro";
public static final String TIPO = "tipo";
public static final String TIP0_ CLASSIC0 = " clssicos;
public static final String TIP0_ ESP0RTIV0S = "esportivos";
122 ^ Google An droi d para T ablets
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
123
public static final String TIPO LUX O = "luxo";
public String nome;
public String desc;
public String urlF oto;
public String urllnfo;
}
Agora note no cdigo-fonte da classe mgica DownloadlmagemU til que tambm
existe um H ashM ap, cujo objetivo justamente manter essas imagens em memria
para fazer um cache eficiente.
private H ashM ap< String, Bitmap> cache = new HashM ap< String, Bitmap> ();
O mtodo download(...) da classe imageDownloader primeiramente tenta buscar a
imagem do cache em memria com a H ashM ap. Caso a imagem no seja encontrada,
o cache do carto de memria sd-card ser utilizado para ler a imagem de algum
arquivo previamente salvo. Se ainda assim a imagem no for encontrada, feito
o download da internet, e ambos os caches so atualizados.
public void download(Activity activity, String uri, ImageView imageView, ProgressBar progress) {
if (cacheO n & & cache.containsK ey (url)) {
// Imagem encontrada no cache. Ento atualiz a o ImageView diretamente
} else {
// Busca as imagens em uma thread separada
// Pode encontrar a imagem no carto de memria ou buscar na internet
// Depois de buscar a imagem, atualiz a o cache
}
}
Agora, se voc depurar esse mtodo, vai verificar que a imagem nunca existe no
cache e que ela sempre est sendo buscada novamente, seja no carto de memria
ou na internet. Por que isso acontece?
A resposta : porque quando ocorre a troca da orientao da tela, a activity
destruda, conforme vimos anteriormente, e, consequentemente, o ListView e o
Adapter para exibir a lista de carros so criados novamente.
Falando mais precisamente, uma nova instncia da classe CarroAdapter ser
criada, e no seu construtor ser criada uma nova instncia de DownloadlmagemU til.
Nesse caso o cache em memria feito com a H ashM ap vai para o espao.
public CarroAdapter(Activity context, List< Carro> carros) {
this.contex t = contex t;
this.carros = carros; *
this.inflater = (Layoutlnflater) contex t.getSy stemService(Contex t.LAY O U T_ INF LATER_ SERVICE);
downloader = new DownloadlmagemU til(contex t);
}
Google An droi d para T ablets
Essa a explicao para as imagens serem lidas novamente. Ao criar uma nova
instncia de DownloadlmagemU til, naturalmente a referncia para a H ashM ap com o cache
de imagens no existe mais.
Ento como resolvemos isso?
Antes de voc pensar em deixar essa H ashM ap esttica, deixe-me propor uma
soluo bastante utilizada em aplicaes Android, que customizar a classe an
droid. app.Application de nosso projeto.
O A ndroid sempre cria uma nica instncia da classe android.app.Application
para cada aplicao que est executando, garantindo que tenhamos um singleton
para nossa aplicao, e se necessrio, podemos utiliz-lo para armazenar outras
informaes.
Para isso precisamos criaruma classe filha de android.app.Application e declar-la
no AndroidManifest.xml da seguinte forma, alterando a tag < application> :
< application android: name= " br. livroandroid .carros. CarrosApplication" android: icon= @ drawable/
icon" android:label="@ string/app_ name' ' android:theme= " @ sty le/tema" >
Vamos utilizar essa ttica para criar um singleton em nossa aplicao. O cdigo-
fonte dessa classe global pode ser visualizada a seguir:
CarrosAppIi cati on .java
public class CarrosApplication ex tends Application {
private static final String TAG = " CarrosApplication;
// Singleton
private static CarrosApplication instance = null;
// Variveis
private DownloadlmagemU til downloader;
public static CarrosApplication getlnstance() {
if (instance == null)
throw new IllegalStateException("Configure a aplicao no AndroidM anifest.x ml" );
return instance;
}
@ 0verride
public void onCreate() {
Log.i(TAG, " Contex tApplication.onCreate()");
downloader = new DownloadlmagemU til(this);
// Salva a instncia para termos acesso como Singleton
instance = this;
}'
public DownloadlmagemU til getDownloadlmagemlltilQ {
return downloader;
}
gO verride
public void onTerminateO {
super.onTerminateQ ;
Log.i(TAGj " Contex tApplication.onTerniinate()");
downloader = null;
}
}
Ao sobrescrever a classe android.app.Application podemos criar um objeto e
deix-lo como um singleton disponvel para nossa aplicao. O mtodo onCreate()
ser chamado uma nica vez durante a inicializao. Note que nesse momento
j aproveitamos para criar os objetos que precisamos deixar como singleton, que
no nosso caso o DownloadlmagemU til.
gO verride
public void onCreateQ {
Log.i(TAG, " Contex tApplication.onCreate()" );
downloader = new DownloadlmagemU til(this);
// Salva a instncia para termos acesso como Singleton
instance = this;
}
Tambm criamos o mtodo getinstance() para obter a instncia dessa classe
global, e o mtodo getDownloadlmagemU til() para obter a referncia do objeto que
queremos deixar na memria, que neste caso a classe mgica que faz os down-
loads das imagens.
Depois de termos implementado a classe global, vamos voltar classe Carro-
Adapter, e em vez de criar uma nova instncia de DownloadlmagemU til como fizemos
anteriormente, podemos utilizar o acesso global para obter esse objeto.
Portanto, vamos alterar o construtor da classe CarroAdapter conforme o demons
trado a seguir.
public CarroAdapter(Activity contex t, List< Carro> carros) {
this.contex t = context;
this. carros = carros;
this.inflater = (Layoutlnflater) contex t.getSy stemService(Contex t.LAY O U T_ INF LATER_ SERVICE);
// Recupera o obj eto global da aplicao
CarrosApplication application = (CarrosApplication) context.getApplication();
// U tiliz a esse obj eto para recuperar a classe que faz o download de imagens
downloader = application.getDownloadlmagemU tilQ ;
}
Cap tulo 3 Con trolan do o estado de sua Acti vi ty 125
126 Google An droi d para T ablets
Dessa maneira teremos uma mxima performance ao girar a tela do celular,
e voc nem vai perceber as imagens serem carregadas novamente, pois elas sero
lidas da memria, diretamente da H ashM ap dentro da classe DownloadlmagemU til.
3.9 Alternando o layout manualmente sem a necessidade de destruir e
recriar a tela - android:configChanges
At o momento vimos como salvar e recuperar o estado da tela para solucionar
o problema de o Android destruir a activity em certos casos.
Mas e se fosse possvel no destruir a activity? E se fosse possvel que os objetos
permanecessem em memria?
Sim, possvel.
Existe a possibilidade de controlar essas alteraes de configuraes de sistema
manualmente e sem a necessidade de destruir a activity Mas nesse caso seremos
os responsveis por alterar o layout e atualiz-lo, tudo manualmente.
A grande vantagem de possuir o controle total que a activity no destru
da e, consequentemente, os objetos permanecem em memria. Portanto, no
necessrio salv-los ou busc-los novamente. Parece uma boa ideia, no ?
Se esse for o seu objetivo, voc ter que ter uma conversa mais ou menos assim
com o nosso simptico sistema operacional do robozinho verde:
Desenvolvedor: Fala, Android. Como est?
Android: Opa, tranqilo! No que posso ajudar?
Desenvolvedor: Gostaria de lhe pedir para controlar a tela manualmente. Sou
um desenvolvedor avanado e gostaria de ter o controle total, tudo bem?
Android: Tudo bem, mas a responsabilidade sua, ok?
E depois de alguns segundos o Android ainda tentar te aconselhar, sendo sua
ltima chance de voltar atrs.
Android: Espero que voc saiba o que est fazendo...
Utilize uma classe global de aplicao quando for necessrio manter
o estado de algum objeto em memria. Criar uma classe filha de
android.app.Application uma boa maneira de fazer singletons no
Android.
Cap tulo 3 Con trolan do o estado de sua Activity
127
E voc responder confiantemente:
Desenvolvedor: Deixa comigo! Obrigado.
Pronto, est feito o estrago!
Brincadeiras a parte, controlar a troca de orientao ou outras alteraes de
sistema manualmente pode ser bom ou ruim, dependendo do caso. Mostraremos
aqui como fazer, e caber a voc decidir quando utilizar.
Primeiramente necessrio adicionar o parmetro android:configChanges =
orientation" dentro da tag < activity > para informar ao Android que vamos con
trolar tudo manualmente. Dessa forma, todas as vezes que houver uma alterao
nas configuraes de sistema o Android vai chamar o mtodo onConfigurationChanged
(configuration) na sua activity, para te avisar sobre qual configurao foi alterada.
Existem vrios eventos de configurao que podem ser monitorados, mas es
tamos interessados no caso mais comum, que o orientation, que ocorre ao trocar
a orientao do celular.
Para o prximo exemplo configure a activity com o parmetro android: configChanges
= " orientation" conforme demonstrado a seguir.
< activity android:name= .activity .TelalistaCarros" android:label= " @ string/app_ name
android: configChanges= "orientation" />
Depois de fazer essa simples alterao, execute a aplicao, e conforme podemos
verificar na figura 3.6, tudo continuar funcionando perfeitamente.
AUDI 6T Spyder >
PorschePanamera >
| Lamborghini Aventador >
Chevrolet CorvetteZ06 >
BMWMS >
Renault MeganeRSTrophy >
' a.
' <
C3
ui
O
u.
" vroAidrotd -Todos os direitos reservados
Figura 3.6-Aplicao com a lista de carros.
n
m
i

!
(
'

:
Mas vamos posicionar o aparelho na horizontal e observar o que acontece. Como
esperado, a activity no destruda, e obviamente a list continua em memria.
Mas veja o resultado na figura 3.7. Consegue perceber algo de estranho?
128 Google An droi d para T ablets
. Hll 9 9:19
v* . Carros Showcase
Ferrari FF >
AUDI GTSpyder >
Porsche Panamera
Lamborghini Aventador
UvroAndroid-Todos os direitos reservados -
Figura 3.7-Aplicao na horizontal, mas com layout incorreto.
Note que a orientao mudou para a horizontal, mas o layout da tela no est
correto. Lembre-se que anteriormente customizamos a aplicao para no exibir
o rodap na horizontal, e tambm deveria ter aparecido a descrio do carro.
O que aconteceu que o Android passou a responsabilidade de atualizar a tela
para o desenvolvedor, e agora somos ns que precisamos fazer todo o trabalho.
Mas para nos ajudar o Android ainda nos avisa sempre que uma alterao
de configurao alterada e chama o mtodo onConfigurationChanged(configuraiion) da
classe Activity , o qual podemos utilizar para monitorar as alteraes. Para corrigir
o exemplo devemos inserir esse mtodo em nossa activity para atualizar a tela
manualmente.
(Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.lay out.carros);
listview = (ListView) findViewBy Id(R.id.listview);
listview.setO nltemClick Listener(this);
if (carros != null) {
listview.setAdapter(new CarroAdapter(this, carros));
}
}
Note que no cdigo chamamos o mtodo setContentView(R.lay out.carros) nova
mente, o que faz com que o novo layout seja aplicado na tela, desta vez utilizando
a pasta /res/layout-land. Tambm foi necessrio atualizar o ListView que estava na
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
129
tela para que o novo layout fosse aplicado. necessrio que se faa isso manual
mente para todas as views presentes na tela, uma por uma.
Note que, se necessrio, voc ainda pode monitorar qual a orientao da tela,
para implementar uma lgica diferente, dependendo da orientao atual.
if (cfg.orientation = Configuration.O RIENTATIO N_ PO RTRAIT) {
Log.i(TAG, "onConfgurationChanged(). Trocou-para Vertical" );
} else if (cfg.orientation = = Configuration.O RIENTATION_ LANDSCAPE) {
Log.i(TAG, "onConfigurationChanged(). Trocou para H oriz ontal");
}
Depois dessa alterao podemos testar a aplicao novamente, e tudo estar
funcionando. A grande vantagem dessa implementao que os objetos perma
necem em memria, inclusive a prpria activity, o que melhora a performance
ao girar a tela.
3.10 0 problema com a configurao do android:configChanges
Gerenciar a troca de layout manualmente pode ser uma boa ideia a princpio,
mas vai depender do caso.
Nesse exemplo simples onde tnhamos um nico componente na tela e uma
lista de carros, at que foi uma boa ideia. A primeira vantagem a facilidade de
implementao, pois foi necessrio escrever menos cdigos. A segunda vantagem na
turalmente a performance, uma vez que a mesma instncia da activity foi utilizada.
Mas e se nossa tela fosse mais complexa? Nesse caso teramos que atualizar
diversos componentes na tela, o que talvez nos levasse a duplicar algum cdigo
em determinado momento.
Agora vamos falar dos aspectos negativos dessa soluo.
No o caso dessa aplicao que fizemos, mas digamos que na tela houvesse
um formulrio com alguns campos de texto para que se digitassem algumas
informaes. Como voc j sabe, por padro o Android salva os textos digitados
e o estado desses componentes. Ento, ao girar a tela voc pode ficar tranqilo,
pois esses textos digitados no sero perdidos.
Oops! Voc podia ficar tranqilo at o momento em que o Android estava
tomando conta do trabalho. Mas agora voc, e adivinha o que vai acontecer?
Esses campos vo perder o valor, e voc ter que atualizar os valores de cada
um deles, um por um.
130 Google An droi d para T ablets
Resumindo, utilize essas opes se voc souber o que est fazendo, seno
melhor deixar o Android fazer o trabalho pesado para voc. Cabe a ns, desenvolve
dores, apenas salvar e recuperar o estado da tela com os mtodos que temos na API,
como, por exemplo, o onSavelnstanceState(bundle) e o onRetainNonConfigurationInstance().
Na prtica, controlar o estado manualmente pode ter as suas vantagens, mas
mais cedo ou mais tarde. isso trar mais problemas do que ajuda. Portanto,
recomendado que se acostume com o ciclo de vida de uma activity, conforme
apresentamos aqui, e que se implemente corretamente os mtodos que salvam
e recuperam o estado de sua tela. At porque o mtodo onConfigurationChanged
(configuration) ser chamado apenas quando alguma alterao nas configuraes
de sistema for detectada, e no quando sua activity for literalmente destruda
por outros motivos, como, por exemplo, se o usurio deixar sua aplicao em
segundo plano por muito tempo, o que pode levar o Android a matar o processo
para liberar memria.
Outro caso em que O mtodo onConfigurationChanged(configuration) no chamado
se outra activity assume o topo da pilha, como quando uma ligao telefnica
recebida. Nesse caso o mtodo onSavelnstanceState(bundle) ser chamado, mas no
o onConfigurationChanged(configuration), uma vez que nenhuma configurao mudou.
Portanto, sendo que o mtodo onSavelnstanceState(bundle) cobre a maioria dos casos,
recomendado que voc se acostume com ele.
3.11 Smartphones com teclado
Smartphones com teclado fsico como o popular Motorola Milestone tambm
sofrem do problema da troca de configurao, que neste caso pode ser a abertura
ou o fechamento do teclado fsico.
Se voc possui um celular com teclado fsico, est com a nossa aplicao dos
carros na vertical e abrir o teclado, advinha o que vai acontecer? Boom! A activity
ser destruda e criada novamente. Sim, acontecer a mesma coisa que acontece
ao girar o celular entre vertical e horizontal, e tudo que vimos at o momento de
salvar o estado da tela se aplica da mesma forma.
Mas com relao ao mtodo onConfigurationChanged(configuration), caso desejemos
interceptaj essas alteraes da configurao do teclado, devemos declar-la no
AndroidManifest.xml, da mesma forma que fizemos com a troca de orientao.
At o momento colocamos a seguinte configurao em nossa activity (se voc
quiser controlar a tela manualmente, lembre-se):
< activity android:name= " .activity .TelaListaCarros" android:label= " @ string/app_ name"
android:configChanges=orientation" >
Dessa forma estamos interceptando manualmente somente a troca de orien
tao, mas at o momento nenhuma outra configurao.
Nesse caso da abertura do teclado podemos alterar a configurao de nossa
activity e adicionar o evento k ey boardH idden da seguinte forma:
< activity android:name= " .activity .TelaListaCarros" android:label= " @ string/app_ name"
android:configChanges= " orientation| k ey boardH idden" >
Com essa configurao estamos solicitando ao Android que no destrua a tela
em trocas de orientao ou ao manusear o teclado.
3.12 A tela de aguarde durante a busca dos carros
Para a busca dos carros implementamos a interface Transacao em nossa classe, e ao
utilizar o mtodo startTransacao(transacao) internamente, a classe Asy ncTask utilizada.
Isso foi explicado no captulo 2 durante a construo de nosso projeto.
Para relembrar esse mtodo vamos conferir o cdigo da classe LivroAndroidActivity .
f e Li vroAn droi dActi vi ty.java
public class LivroAndroidActivity ex tends Activity {
protected void alert(int mensagem) {
AndroidU tils.alertDialog(this, mensagem);
}
// Inicia a thread
.public void startTransacao(Transacao transacao) {
boolean redeO k = AndroidU tils.isNetwork Available(this);
if (redeOk) {
// Inicia a transao
TransacaoTask task = new TransacaoTask (this, transacaO j R.string.aguarde);
task .ex ecute();
} else {
// No ex iste conex o
AndroidU tils.alertDialog(this, R.string.erro_ conex ao_ indisponivel);
}
}
}
Basicamente, o que esse mtodo faz delegar a execuo da transao para
a classe TransacaoTask , que um Asy ncTask e controla toda a execuo da thread e
Cap tulo 3 Con trolan do o estado de sua Acti vi ty 131
132 Google An droi d para T ablets
a atualizao da view. Mas o importante que dentro dela existem os mtodos
para abrir ou fechar o ProgressDialog, para exibir ao usurio uma janela com a
mensagem de aguarde.
public void abrirProgress() {
try {
progresso = ProgressDialog.show(contex t, contex t.getString(aguardeH sg))j
} catch (Throwable e) {
Log.e(TAG, e.gefflessage(), e);
}
}
public void fecharProgress() {
try {
if (progresso != null) {
progresso.dismissO ;
}
} catch (Throwable e) {
Log.e(TAG, e.get".essage(), e);
}
}
Essa janela de aguarde ser fechada depois de executar a transao normalmente.
Um problema comum acontece quando o usurio gira a tela durante a exe
cuo da transao.
Nesse caso, o que acontece com a transao que est executando com o Asy nc-
Task e o ProgressDialog que est sendo exibido?
Se nossa activity declarar o atributo android:configChanges, no precisamos nos
preocupar com isso, pois, conforme vimos anteriormente, nesse caso o Android
no vai destruir a tela, e tanto o Asy ncTask quanto o ProgressDialog podem continuar
executando sem problemas.
< activity android:name= " .activity .TelaListaCarros" android:label= " @ string/app_ name" android:con
figChanges= " orientation| k ey boardH idden" >
Mas o problema se nossa activity no declara esse atributo e deixamos o
Android fazer o ciclo de vida normal, que seria destruir a activity e criar otra
logo em seguida. Nesse caso j vimos como salvar o estado dos objetos, mas um
problema que teramos seria em salvar o estado da janela de aguarde.
Nesse caso especfico comum os desenvolvedores enfrentarem o erro demons
trado na figura 3.8. Esse erro ocorre ao girar o aparelho durante a execuo de
uma transao, porque quando ela termina, a activity anterior j foi destruda, e
dessa forma, ao fechar o ProgressDialog a referncia de sua activity no existe mais.
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
133
fc&dro.
odro.
Acx lro.
Aodrc
tidro.
Aodro.
Ck dro.
Ao dro . .
i n d r o . .
kttiro.
l r . d r o ..
Andro . .
Arj dro. .
Aodr..
Axdro..
edr o. .
Acti-
k t i v
F iTAL ESCEPT 1 0 X : * d
A t 4n d r o i d , v i v . 9iD(So IloM 9e r I k p l . < i D d 7ievLoc)c d(9& nd9v X s 9 r Z k p 4 o T * 3SS)
At a n d r o i d . < rie*. BittdowH aaa^ erlmpl . T e f c o r e ^ i e v VindowX aaASej lApl. j avo ? 200)
a t n d r o i d . v i a v . i D d o * * l o c * l V i n d o v K i i 9tt-. hovo? iev(ViBdoif.3 * 4 :432)
A t a n d r o i d . a p p . D i a l o g . d i s s i s a D i a l o l D i a l r . j ova; 278)
A t w i d r o i d .t p p . D U l o g . * c e e 9O 0Q (Dialog. j v * : ? l|
at a n d r o i d . a p p . D i a l o g l . r u n ( D i a l o g . j a v a : 111)
At AAdroid.e*.H A*dlttr.ba& dioCAllb*ek (Bai> dl r. } v * : S 87)
a t Aivdroid.aA.Eaj & dler.di s p A t c h M e s s a g e t B a o d l e r . j a v a i S Z )
t a n d r o i d . * Looper. l c o p f l o o p w . } * v a : 130)
at a n d r o i d . a p p . A c t i r i t y T h r e a d aaic( A c t i v i t y T f e r e a d . j a v a . 3683)
A t )ava. l a a . r e t l e c t . K etfeod, i D v o t a } U t i * e ( R t l v 6 K e thod)
at j a v & . l a a g T e f l e c t . & e t b o d . i n v o f e e ( X e t h o d . j a v a : S 0? )
At c o * . a & d r o i d .i & t o r & A l .os .Zy otel& itiK etbodAnidATsCal l e r .r u B t Z r e o t e l b i t )* v a 63$)
A t com.android internai.oa.ZT^ oloInit. ain(Zy goteIait.j a v a :597)
At d a l v i k . s r * t e * - 0otiveSt*j rt.k 4 in(l< at& ? e K e t b o d )
F orce inisliixig activity br livroandroid carros/' .TeialistaC-arrss
Activity paus tl*ec*J t or K isiorv ccord(4S? S? ? 30 br liVM M ;*di-eid carro*' Te-lal;stACAr: )
Figura 3.8 - Erro do ProgressDialog ao trocar de orientao.
Para solucionar o problema at possvel salvar o estado da prpria transao,
e existem debates sobre isso em grupos de discusso na internet, mas particu
larmente prefiro, nesse caso, apenas cancelar o Asy ncTask e fechar o ProgressDialog.
Para isso vamos alterar o cdigo da classe LivroAndroidActivity e verificar se nossa
transao est executando quando o Android for destruir a activity, e nesse caso
vamos cancelar a thread dentro do Asy ncTask e tambm fechar o ProgressDialog.
O cdigo-fonte completo dessa classe pode ser visualizado a seguir.
l Li vroAn droi dActi vi ty.java
public class LivroAndroidActivity ex tends Activity {
private TransacaoTask task;
protected void alert(int mensagem) {
AndroidU tils.alertDialog(this, mensagem);
}
// Inicia a thread
public void startTransacao(Transacao transacao) {
boolean redeO k = AndroidU tils.isNetwork Available(this);
if (redeO k) {
// Inicia a transo
task = new TransacaoTask (this, transacao,R.string.aguarde);
task .ex ecuteQ ;
} else {
// No ex iste conex o
Androidlltils.alertDialog(this, R.string.erro_ conex ao_ indisponivel);
}
134
Google An droi d para T ablets
(JOverride
protected void onDestroy() {
super. onDestroy Q ;
if (task != null) {
boolean ex ecutando = task .getStatus().eq uals(Asy ncTask .Status.RU NNING);
if (ex ecutando) {
task .cancel(true);
task .fecharProgressQ ;
}
}
}
}
Dessa forma, ao girar o aparelho o Android vai reiniciar a activity para controlar
o ciclo de vida. A Asy ncTask ser cancelada, e o ProgressDialog tambm ser fechado
corretamente.
Naturalmente, ao iniciar a nova instncia da activity a transao voltar a
executar, e novos Asy ncTask e ProgressDialog sero utilizados. Mas isso melhor do
que deixar a tarefa executando ou correr o risco de travar a aplicao ao fechar
O ProgressDialog.
Lembre-se que sempre que a activity declara o atributo android:configChanges= " o
rientation" , no precisamos nos preocupar com isso, pois ela no ser destruda.
< activity android:name= " .activity .TelaListaCarros" android:label= " @ string/app_ name"
android:configChanges=orientation| k ey boardH idden" >
Nesse caso, corrigimos um problema de manter a transao executando du
rante a troca de orientao, mas ganhamos a complexidade a mais de controlar a
atualizao de layout no mtodo onConfigurationChanged(configuration).
(W verride
public void onConfigurationChanged(Configuration newConfig) {
super. onConfigurationChanged(newConfig);
setContentView(R.lay out.carros);
listview = (ListView) findViewByld(R .id.listview);
listView.setO nltemClick Listener(this);
if (carros != null) {
listview.setAdapter(new CarroAdapter(this, carros));
}
}
Nesse nosso projeto deixaremos essa configurao.ativada no AndroidManifest.
xml, sendo que a performance durante a troca de orientao melhora, e em nosso
exemplo a implementao foi simples.
Cap tulo 3 Con trolan do o estado de sua Acti vi ty
135
Mas tambm vamos deixar o mtodo onSavelnstanceState(bundle) implementado,
pois este ser chamado quando o Android levar nossa aplicao para segundo
plano, como, por exemplo, se o usurio pressionar a tecla H ome ou receber uma liga
o. Assim, quando voltar para a aplicao, os dados estaro l, salvos, mesmo se
houver a necessidade de recriar a activity por questes internas de gerenciamento
de memria.
A seguir podemos validar a implementao final da classe TelaListaCarros deste
captulo e o correto gerenciamento do ciclo de vida da activity
T elaLi staCarros.java
public class TelaListaCarros extends LivroAndroidActivity implements O nltemClick Listener, Transacao
{
private static final String TAG = " livroandroid;
private Listview listview;
private List< Carro> carros;
private String tipo;
@ SuppressW arnings(" uncheck ed)
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
tipo = getIntent().getStringEx tr3(" tipo);
listview = (Listview) findViewBy Id(R.id.listview);
listview.setO nltemClick Listener(this);
carros = (List<Carro>) getLastNonConfigurationlnstanceQ ;
Log.i(TAG," Lendo estado: getLastNonConfigurationInstance()");
if (carros == null 8& savedlnstanceState != null) {
// Recuperamos a lista de carros salva pelo onSavelnstanceState(bundle)
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
Log.i(TAG," Lendo estado: savedlnstanceState(carros)" );
this.carros = lista.carros;
}
if (carros != null) {
// Atualiz a o Listview diretamente
listview.setAdapter(new CarroAdapter(this, carros));
} else {
startTransacao(this);
}
}
U J
Ci
!.
-
C
A
M
P
U
S

F

R
T
A
L
t
Z
A

I

B
I
B
L
I
O
T
E
C
A

'
136
Google An droi d para T ablets
(SOverride
public O bj ect onRetainNonConfigurationInstance() {
Log.i(TAG, " Salvando Estado: onRetainNonConfigurationInstance()" );
return carros;
}
(SOverride
protected void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
Log.i(TAG," Salvando Estado: onSavelnstanceState(bundle)'' );
// Salva o estado da tela
outState.putSerializ able(ListaCarros.KEY, new ListaCarros(carros));
}
gO verride
public void ex ecutarQ throws Ex ception {
// Busca os carros em uma thread
this.carros = CarroService.getCarros(this, tipo);
}
(SOverride
public void atualiz arViewQ {
// Atualiz a os carros na thread principal
if (carros l - null) {
listview.setAdapter(new CarroAdapter(this, carros));
}
}
(SOverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapter().getItem(posicao);
Intent intent = new Intent(this, TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , c);
startActivity (intent);
}
(SOverride
public void onConfigurationChanged (Configuration newConfig) {
super. onConfigurationChanged (newConfig);
setContentView(R.lay out.carros);
listview = (Listview) findViewBy Id(R.id.listview);
listview.setO nltemClick Listener(this);
if (carros != null) {
listview.setAdapterfnew CarroAdapter(this, carros));
}
}
}
1
j.-sanifcwaifiSMiaB
rnsm am
CAPITULO 4

Trabalhando com
diversos tamanhos de
tela
O Android um sistema operacional para dispositivos mveis extremamente
flexvel e permite que a aplicao funcione em diversos aparelhos com diferentes
tamanhos e resolues de tela.
Existem celulares com Android que possuem telas pequenas, mdias, grandes,
e tambm temos os tablets, os quais possuem um grande espao disponvel na
tela, possibilitando criar aplicaes diferenciadas para essas telas.
Agora tambm temos o Google TV, onde o Android se adapta incrivelmente
em grandes televises Full HD diretamente em sua sala de estar.
Neste captulo vamos estudar como criar telas que funcionem corretamente
em vrias resolues diferentes e como customiz-las quando necessrio.
4.1 I ntroduo
At o Android 1.5 existiam apenas smartphones Android com o mesmo tamanho
de tela, e os desenvolvedores no tinham a preocupao de criar aplicaes que
funcionassem de forma consistente em telas com diferentes tamanhos e resolues.
Esses smartphones com Android 1.5 possuam uma tela HVGA de 320 x 480
pixels, e alguns dos mais conhecidos so o HTC Gl, o HTC Magic, o HTC Hero,
o Motorola Dext, entre outros.
Posteriormente foram surgindo aparelhos com telas maiores e com uma melhor
resoluo. Dessa forma, foram adicionados na plataforma do Android recursos
138 Google An droi d para T ablets
1
para ajudar o desenvolvedor a criar e organizar a aplicao para que ela funcione
perfeitamente em todos esses aparelhos. Cabe a ns, desenvolvedores, conhecer
os truques e melhores prticas para desenvolver aplicaes que tenham um bom
visual e interatividade com o usurio, independente do tamanho da tela.
4.2 Exemplo prtico sobre o problema de resoluo
Antes de iniciarmos a teoria vamos verificar ura exemplo prtico, onde uma apli
cao mal projetada pode ter resultados diferentes, dependendo da resoluo da
tela do aparelho.
Para isso vamos criar um projeto com as seguintes configuraes:
Nome do projeto: LivroAndroid-Cap04-DiversasResolucoes
Verso: Android 1.6 ou superior
Pacote: br.livroandroid.telas.cap04
Criar activity: M ain
Verso mnima do SDK: 4 (Android 1.6)
Feito isso, podemos alterar a activity Maincriada automaticamente no projeto,
para utilizar o seguinte layout de tela, onde teremos duas simples imagens, uma
abaixo da outra.
(Si /res/layo ut/exe m p! o_p i xel_d p.xm I
< ? x ml version= " 1.0" encoding= utf-8" ? >
LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android:layout_width= "fill j iarent"
android: lay out_ height= " fill_ parent"
android :background=''#ffffff"
>
< ImageView
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:src= " @ drawable/icon"
android:lay out j narginLeft= " 100px "
/>
< ImageView
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
B
android:src= " @ drawable/icon"
android:lay outj narginleft= " 100dp" />
< /LinearLay out>
Simples, no ? Pois , mas agora vamos ver o primeiro erro clssico de muitos
desenvolvedores que esto iniciando no mundo do Android.
Note que criamos duas imagens exatamente iguais, mas com uma pequena
diferena no valor do atributo android:lay out_ marginLeft, que define a margem da
esquerda. Na primeira imagem utilizamos a notao I00px de pixels, e na segunda
utilizamos a notao l@ 0dp de density-independent pixel.
Para testar a tela voc pode utilizar a prpria pr-visualizao do editor dentro
do Eclipse. Ao selecionar na visualizao o tipo de tela HVGA ADP1 (Android
Developer Phone 1) voc ver as duas imagens uma embaixo da outra, assim como
o esperado. Mas ao selecionar o tipo de tela WVGA (Nexus One) voc ver que a
segunda imagem foi deslocada um pouco para a direita e perdeu o alinhamento
com a primeira.
Se quiser testar no emulador, crie dois AVDs (Android Virtual Device) diferen
tes. O primeiro pode ter uma tela padro HVGA, e outro, uma tela maior WVGA.
Ao criar o AVD escolha a verso 1.6 ou superior do Android.
A figura 4.1 exibe o resultado no primeiro emulador com uma tela HVGA
padro de 320 x 480 pixels.
Cap tulo 4 T rabalhan do com di versos taman hos de tela
BB 4:16PM
Figura 4.1 - Exemplo em uma tela padro HVGA de 320 x 480 pixels.
140 Google An droi d para T ablets
A figura 4.2 exibe a mesma aplicao no segundo emulador com uma tela
WVGA de 480 x 800 pixels.
Mas o que aconteceu com esse exemplo? Ns sabemos que a segunda tela maior
que a primeira, mas por que as imagens no ficaram alinhadas? Para piorar a situ
ao, o que acontecer se executarmos esse mesmo exemplo em uma tela pequena?
Para tirar a dvida vamos criar um AVD com Android 1.6 ou superior e com
uma tela QVGA de 240 x 320 pixels. A figura 43 exibe o resultado ao executar
mos a mesma aplicao, mas agora em uma tela pequena. E novamente temos
um resultado diferente.
4r13.PM
S
5
Figura 4.2 - Exemplo em uma tela WVGA de 480 x 800 pixels.
Sffl S22PM,
Cap tulo 4 T rabalhan do com di versos taman hos de tela 141
Figura 4.3 Exemplo em urna tela QVGA de 240 x 320 pixels.
Para facilitar a visualizao vamos exibir os trs exemplos na seguinte ordem:
tela pequena QVGA, tela mdia HVGA e tela grande WVGA.
a s a 4:13PM
Figuras 4.1, 4.2 e 4.3 - Lado a lado, para comparao.
E agora, o que fazemos?
Solucionar o problema simples, basta seguir a regra de nunca utilizar a no
tao px de pixels e sempre preferir a notao dp de density-independent pixel.
Dessa forma, altere a margem da primeira imagem tambm para a notao dp
utilizando o atributo android:lay out_ inarginLeft= ' ' 100dp" .
142 Google An droi d para T ablets
Ao fazer isso o exemplo funcionar da mesma forma nos trs emuladores,
conforme podemos visualizar nas figuras 4.4, 4.5 e 4.6.
5:31 PM
Figuras 4.4, 4.5 e 4.6 - Emulador pequeno QVGA, mdio FVGA e grande WVGA.
Assim, mesmo os emuladores tendo diferentes tamanhos de tela e tambm
resolues diferentes, o resultado igual em todos. Note que visualmente o espao
da margem o mesmo em cada tela.
Portanto, a regra simples: utilize a notao dp (density-independent pixel) e
nunca px (pixels), assim o Android vai tomar conta de todo o trabalho e vai ga
rantir que os tamanhos utilizados sero os mesmos, independente do tamanho
fsico ou da resoluo da tela.
Agora que a introduo ao problema foi feita e a soluo para esse problema foi
apresentada, vamos voltar teoria e verificar alguns aspectos importantes, assim
como a terminologia utilizada pelo A ndroid para categorizar as telas, levando em
conta o seu tamanho fsico, a resoluo de tela, entre outros.
Assim que apresentarmos as terminologias utilizadas, voltaremos a este
exemplo para entender por que os valores definidos em pixels tiveram resultados
diferentes.
Cap tulo 4 T rabalhan do com di versos taman hos de tela
143
4.3 Termos e notaes utilizadas
Para criar aplicaes que funcionem nas mais diversas telas, necessrio entender a
nomenclatura utilizada para categorizar cada uma delas, seja por tamanho fsico ou
por resoluo, para que possamos customizar a nossa aplicao da maneira correta.
A seguir veremos detalhadamente cada termo utilizado pela plataforma.
4.3.1 T aman ho de tela (screen si ze)
Essa medida refere-se ao tamanho fsico da tela, medida na diagonal. A plataforma
atualmente separa os tamanhos fsicos de tela em quatro diferentes categorias:
tela pequena small, tela normal normal, tela grande large, tela extragrande x large.
Para deixar mais clara a comparao de cada nomenclatura com aparelhos
reais, veremos a seguir uma tabela que exibe cada categoria e alguns celulares
que se enquadram nela.
small: Podemos inserir nesse grupo o celular Sony Ericsson XPeria Mini,
que possui uma pequena tela QVGA de 240 x 320 pixels.
normal: Celulares como o primeiro HTC Gl, o HTC Magic e o Motorola
DEXT possuem uma tela HVGA mdia de 320 x 480 pixels. Nesse grupo
tambm se encontram os celulares um pouco maiores e tambm com me
lhor resoluo, como os aparelhos Nexus One, Sony Ericsson XPeria X10,
Motorola Milestone e Samsung Galaxy S, os quais possuem uma tela WVGA
que varia entre 480 x 800 e 480 x 854 pixels.
large: Os tablets como o Samsung Galaxy Tab de 7 possuem uma tela
grande WVGA de 1024 x 600 pixels.
x large: Novos tablets com Android 3.x, como o Motorola Xoom de 10,
possuem uma tela WXGA de 1280 x 800 pixels.
Dessa forma, ao desenvolver as aplicaes podemos criar arquivos especficos
para determinados tamanhos de tela. Por exemplo, digamos que o objetivo seja
customizar a aplicao para um celular com uma tela pequena. Podemos utilizar
recursos especficos, como imagens, arquivos de layouts, textos e tudo a que temos
direito, mas especificamente para telas pequenas. Para isso basta criarmos pastas
com as seguintes nomenclaturas:
Essas medidas de tela so aproximadas e podem ter variaes.
144
Google An droi d para T ablets
/res/layout-small: Arquivos de layout customizados para telas pequenas.
/res/values-small: Arquivos de textos e outros customizados para telas pe
quenas. '
O Android vai tomar conta do resto, e se existir um arquivo especfico para o
tamanho de tela do aparelho, esse arquivo ser utilizado, em vez do arquivo padro.
Vamos exemplificar. Digamos que temos um texto a ser exibido na tela e que
tenhamos que alterar esse texto para que seja diferente em celulares com uma
tela pequena. Para isso basta criarmos o arquivo /res/values-small com a mensagem
personalizada.
Digamos que temos o seguinte arquivo com mensagens:
/res/values/stri n gs.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< resources>
< string name= " hello" > H ello World, M ain!< /string>
< string name= " app_ name" > Telas< /string>
< string name= " mensagem" > Este emulador legal.< /string>
< /resources>
Ento vamos alterar nosso exemplo anterior para exibir uma mensagem
em cima das duas imagens. Para isso vamos criar um Textview com a mensagem
@string/mensagem.
i / res/layo ut/exem plo_pi xel_d p.xm I
< ? x ml version= " 1.0encoding= " utf-8" ? >
< LinearLay out x mlns: android= " http: //schemas. android. com/apk /res/android"
android:orientation= " vertical"
android: lay out_width=" fill_parent"
android: lay out_height= "fill_ parent"
android:back ground= " # ffffff"
>
< Iex tView
android :layout_width=''fill_parent"
android:lay out_ height= wrap_ content"
android:tex t= " @ string/mensagem"
android:tex tColor= " # 000000"
/>
< ImageView
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content
Cap tulo 4 T rabalhan do com di versos taman hos de tela 145
android: src= " @ drawable/icon"
android:lay out j narginLeft= " 100dp'
/>
<ImageView
android:lay out_ width= wrap_ content"
android: lay out_ height= ' wrap_ content''
android:src=" (drawable/icon"
android:lay out_ marginLeft= " 100dp"
/>
< /LinearLay out>
A figura 4.7 exibe esse exemplo sendo executado em um emulador com uma
tela normal HVGA;
Agora, se for necessrio customizar o texto para telas pequenas, podemos criar
um arquivo personalizado utilizando a pasta /res/values-small. Note que no pre
ciso customizar todas as mensagens, e sim somente aquelas que forem necessrias.
l /res/values-small/stri n gs.xml
<?xml version= " 1.0encoding=' 'utf-8?>
< resources>
< string name=" mensagem' '>Este emulador legal. peq ueno mas continua legal.< /string>
< /resources>
E isso tudo! Agora, ao executarmos a mesma aplicao em um emulador
Q VGA com tela pequena, vamos visualizar a nossa mensagem customizada, como
podemos verificar na figura 4.8.
7:15 PM
Este emulador legal.
Figura 4.7 - Exemplo executando em uma tela normal.
146 Google An droi d para T ablets
_e>
- QHDa7:i8PM
Este emulador legal. pequeno mascontinua
legal.
Figura 4.8 - Exemplo executando em uma tela pequena.
Se for necessrio a customizao de um layout, recomendado que
os arquivos de layout sejam declarados pelo seu tamanho fsico
(small, normal, large, xlarge), como por exemplo /res/layout-small/
tela.xml ou /res/layout-xlarge/tela.xml.
4.3.2 Proporo da tela (aspect rati o)
O termo aspect ratio representa a proporo entre a altura e a largura da tela. No
A ndroid essas telas podem ser agrupadas por aspect ratio como long ou notlong.
As telas definidas como normais so aquelas H VGA de 320 x 480 pixels e so
categorizadas como notlong. Essas telas so definidas como a referncia padro
para a comparao com outros tamanhos de tela.
O outro grupo que temos so os celulares identificados com uma tela do tipo
long. Para ser definida como long uma tela deve ser considerada comprida quando
est na vertical e larga quando est na horizontal.
Ao definir o aspect ratio da tela comum vermos as notaes de que
essa tela de 3:2 ou 4:3. Por exemplo, uma tela H VGA de 320 x 480
pixels possui a proporo de 3:2, enquanto uma tela W VGA de 480 x
800 pixels possui uma proporo de 4:3.
Naturalmente os tablets so categorizados como long, mas os smartphones
como o Nexus S, Motorola Milestone e similares tambm entram nessa categoria.
Note que esses smartphones com uma melhor resoluo, como o caso do
Nexus S, esto no mesmo grupo do primeiro celular HTC Gl, se for comparado
o tamanho fsico da tela. Nesse caso, ambos foram categorizados com uma tela
de tamanho mdio, definida pela plataforma como normal.
Mas aqui estamos comparando o aspect ratio e, nesse caso, esses dois smar
tphones entram em grupos diferentes. A tela do HTC G1 considerada uma
tela com proporo normal e categorizada como notlong. Mas a tela do Nexus
S categorizada como long, pois ela comprida quando est na vertical e larga
quando est na horizontal.
Quem faz essas definies o Android. Para ns, desenvolvedores, cabe a
tarefa de utilizar sabiamente esses conhecimentos para customizar os recursos
para cada tela, dependendo de cada caso. Um exemplo que podemos citar aqui
que obviamente uma tela do tipo long possui um maior espao na tela. Ento, se
houver a necessidade, podemos customizar os arquivos para trabalhar de forma
diferente para essas telas.
Da mesma forma que fizemos anteriormente, se necessrio, podemos criar
arquivos customizados por aspect ratio, como, por exemplo, /res/dmwable-long, /
res/layout-long e /res/values-long.
Podemos dizer que os tipos de telas categorizados como normais
e, dessa forma, como aspect ratio notlong, so as QVGA, H VGA e VGA. J
as telas WQVGA, W VGA e F W VGA so consideradas compridas na vertical e
largas na horizontal e so categorizadas como aspect ratio long.
4.3.3 Resoluo da tela
A resoluo de tela expressa em pixels, por exemplo, 320 x 480 px. No Android
temos diversos celulares com resolues diferentes. Assim, no desenvolvimento
dos aplicativos devemos evitar utilizar essa notao para definir os tamanhos das
views, valores de margens, espaamentos e qualquer outro parmetro.
Conforme vimos anteriormente, no Android nunca trabalhamos diretamente
com pixels, e sim com dp (density-independent pixel). Ento vamos verificar o
item densidade logo a seguir.
4.3 . 4 Den si dade
A densidade da tela basicamente a relao entre a quantidade de pixels existentes
na tela com a sua largura e altura. Por definio, quanto maior a densidade, maior
ser a quantidade de pixels espalhados pela tela.
Cap tulo4 T rabalhan do com di versos taman hos de tela ^ 147
No Android temos aparelhos com densidade baixa ldpi, densidade normal mdpi,
densidade alta hdpi e densidade extra-alta xhdpi.
Podemos dizer que at o Android 1.5, existiam apenas celulares de densidade
mdia mdpi, por exemplo, o primeiro HTC Gl, o HTC Hero e o Motorola DEXT.
J aparelhos como o Nexus One, o Sony Ericsson XPeria X10, o Motorola Mi-
lestone, o Samsung Galaxy S, os quais geralmente tm uma tela fsica maior wvga
de 480 x 800 pixels, possuem uma densidade alta hdpi, de forma que so capazes
de exibir uma quantidade maior de pixels na tela.
A densidade basicamente a relao entre a quantidade de pixels .
existentes na tela com a sua largura e altura. Por definio, quanto
maior a densidade, maior ser a quantidade de pixels espalhados
pela tela.
Lembre-se que o termo densidade fala sobre a quantidade de pixels
disponveis na tela, e no sobre o tamanho fsico desta. Podem existir
casos de aparelhos terem o mesmo tamanho fsico, mas densidades
diferentes. A ideia similar de um monitor, onde temos baixas e
altas resolues.
4 .3.5 DIP ou DP (den si ty-i n depen den t pi xel)
O DIP ou DP uma unidade de medida de pixel que foi criada para deixar trans
parente para o desenvolvedor o problema de existirem diversos celulares Android
com diferentes resolues e densidades de tela.
Para exemplificar vamos tomar como base uma tela HVGA de 320 x 480 pixels
com densidade normal. Essa tela era padro e nica at o Android L5 (lembre-se
do HTC Gl).
Em telas normais a unidade dp equivalente a um pixel em uma tela padro
de 160 dpi (dots per Inch). Portanto, repetindo a frase, mas em outros termos, na
plataforma do Android podemos assumir que dp igual a lpx em telas normais
de 160 dpi (dots per Inch).
Ateno! No confunda dpi (dots per Inch) com dp (density-
independent pixel).
A notao pontos por polegada ou, como conhecida em ingls, DPI
(Dots Per Inch) uma medida de densidade amplamente utilizada
e que define a quantidade de pixels por polegada na tela. J DIP
(Density-independent Pixel) ou simplesmente dp uma notao
utilizada pela plataforma do Android para trabalhar com diferentes
densidades de tela.
148 Google An droi d para T ablets
Cap tulo 4 T rabalhan do com di versos taman hos de tela 149
Dadas as definies, vamos prosseguir com o prximo tpico, onde vamos
entender por que px diferente de dp, dependendo do aparelho.
4 .3 . 6 En ten den do o exemplo
Vamos voltar ao primeiro exemplo deste captulo, onde a margem da esquerda
da primeira imagem foi definida incorretamente em pixels, causando um deslo
camento entre ela e a segunda imagem.
Ento vamos verificar novamente as figuras da aplicao executando em um
emulador com densidade pequena, mdia e grande.
Figuras 4.1, 4.2 e 4 3 - Lado a lado, para comparao.
A forma de entender o exemplo simples. Lembre que densidade basica
mente a relao entre a quantidade de pixels existentes na tela com a sua largura
e altura. Por definio, quanto maior a densidade, maior ser a quantidade de
pixels espalhados pela tela.
Dessa forma, aqui temos trs diferentes telas.
A tela do meio exibe um emulador h v g a de 320 x 480 pixels de mdia densidade.
Isso significa que ele segue o padro, e nesse tipo de tela lpx igual a ldp. Por isso,
mesmo escrevendo o cdigo da maneira incorreta, ele continua funcionando.
Mas em uma tela pequena, onde existem poucos pixels disponveis, podemos
verificar que lpx maior que ldp. Neste caso foram utilizado I00px para a margem
da esquerda na primeira imagem e I00dp para a segunda. Dessa forma, como nessa
150
Google An droi d para T ablets
Cela de baixa densidade existem poucos pixels, tivemos o problema de I00px ocupar
mais que a metade da tela e ultrapassar o espaamento dado por eedp.
J no terceiro exemplo, onde temos uma tela de alta densidade, podemos
verificar que I00px ocuparam um menor espao na tela do que I00dp. Isso ocorre
porque essa tela possui uma tima definio e uma grande quantidade de pixels
espalhados por ela. Dessa forma, I00px para essa tela foram quase nada (maneira _
de dizer) se comparada com as demais.
Portanto, a medida em pixel vai nos trazer diferentes resultados justamente
pelo fato de termos telas com densidade baixa ldpi, densidade normal mdpi, den
sidade alta hdpi e densidade extra-alta x hdpi, onde cada densidade possui uma
determinada quantidade de pixels disponveis.
O segredo utilizarmos a notao dp (density-independent pixel) e deixarmos
que o prprio Android se encarregue de realizar a converso necessria de pixels
para o tamanho que desejamos.
Ao utilizar I00dp o Android saber que um celular de baixa densidade possui
menos pixels e que um celular de alta densidade possui mais, e vai automatica
mente fazer a converso de proporo, para que o resultado visual seja o mesmo.
4.4 Convertendo pixels em dp e vice-versa
Primeiramente vamos pensar que precisamos criar um layout na tela e que este
precisa ocupar a tela inteira. J sabemos que para isso devemos utilizar a notao
match_ parent ou fill_parer>t (ambos so iguais, mas fill_ parent est deprecated).
Mas digamos que estamos criando uma view customizada e precisamos de
senhar um quadrado na tela. Para isso precisamos utilizar a API de baixo nvel
de desenho, conforme demonstrado a seguir.
@ 0verride
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, 320 / 2, 480 / 2, paint)j
}
Foi necessrio passar como parmetro o tamanho do quadrado e um valor
fixo de pixel.
Cap tulo 4 T rabalhan do com di versos taman hos de tela 151
Ns sabemos que existem telas H VGA de 320 x 480 pixels e, portanto o quadrado
vai ocupar a metade do tamanho da tela. Mas o que acontece se executarmos o
mesmo exemplo em uma tela de maior densidade, como uma w v g a de 480 x 800
pixels? Se voc j entendeu como funciona, saber que o quadrado no vai ocupar
a metade da tela, e ficar menor, uma vez que essa uma tela de alta densidade,
com muitos pixels.
Vamos criar o exemplo completo e testar em dois emuladores diferentes.
& ExemploCon verterPi xelDPI.java
public class Ex emploConverterPix elDPl ex tends Activity {
gO verride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(new TesteView(this));
}
class TesteView ex tends View {
private Paint paint;
public TesteView(Contex t context) {
super(contex t);
paint = new PaintQ ;
paint.setColor(Color.LTGRAY);
paint.setSty le(Sty le.F ILL);
}
@ 0verride
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(8, 0, 320 / 2, 480 / 2, paint);
}
}
}
As figuras 4.9 e 4.10 exibem o resultado em aparelhos de mdia e alta densidade.
Como podemos verificar na tela h v g a de 320 x 480 pixels, o exemplo funcionou
como o esperado, e nosso quadrado (na verdade um retngulo) preencheu exata
mente a metade da tela. O motivo obvio, pois colocamos o valor fixo em pixels
no mtodo de desenho. Mas ao executar o mesmo exemplo em um aparelho de
alta densidade, o resultado foi diferente, e o quadrado foi reduzido, pois a quan
tidade de pixels maior nessa tela. Para corrigir o exemplo poderamos colocar
valores fixos tambm para respeitar a tela w v g a de 480 x 800 pixels do segundo
aparelho, mas dessa forma iramos estragar o primeiro. E agora, o que fazemos?
152
Google An droi d para T ablets
Figuras 4.9 e 4.10 - Exemplo de desenho executando em telas de mdia e alta densidade.
A soluo pensar que esses valores de 320 x 480 pixels na verdade esto em
dp (density-independent pixel) e fazer a converso do valor em dp para pixels. Se
isso fosse um arquivo XML de layout, ns poderamos utilizar 320dp ou 480dp, e
o resultado seria exatamente o mesmo em telas de densidades diferentes. Mas
aqui, diretamente no cdigo J ava, teremos que fazer a converso manualmente.
Para isso vamos converter os valores 320dp e 480dp para pixels antes de passar
ao mtodo de desenho. O seguinte mtodo demonstra como essa converso
realizada:
public float toPix els(Contex t contex t, float dip) {
Resources r = contex t.getResourcesQ ;
float densidade = r.getDisplay M etrics().density ;
int px = (int) (dip * densidade + 0.5f);
retunn px;
}
iS d ExemploCon verterPi xelDP2.java
public class Ex emploConverterPix elDP2 ex tends Activity {
(SOverride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(new TesteView(this));
}
Cap tulo 4 T rabalhan do com di versos taman hos de tela 153
class TesteView ex tends View {
private static final String TAG = " livroandroid";
private Paint gaint;
public TesteView(Contex t contex t) {
super(contex t);
paint = new Paint();
paint.setColor(Color.LTGRAY );
paint.setSty le(Sty le.F ILL);
}
(SOverride
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Valores em dp
float larguraDP = 320;
float alturaDP = 480;
// Converter para pix els
float larguraPx = toPix els(getContex t()j larguraDP);
float alturaPx = toPix els(getContex t(), alturaDP);
// Desenha o q uadrado
canvas.drawRect(0, B, larguraPx / 2, alturaPx / 2, paint);
// Imprime a densidade da tela e o valor em pixels
Resources r = getContex t().getResources();
float densidade = r.getDisplay M etricsQ .density ;
Log.i(TAG," Densidade da tela: " + densidade);
Log.i(TAG, " 320x 480 dp igual a " + larguraPx + x " + alturaPx + " pixels");
}
}
// Converte de DIP para Pix els
public float toPix els (Contex t contex t, float dip) {
Resources r = contex t.getResourcesQ ;
float densidade = r.getDisplay M etricsQ .density ;
int px = (int) (dip * densidade + 0.5f);
return px;
}
}
Portanto, antes de passarmos os nmeros para o mtodo de desenho estamos
convertendo os valores em dp para pixels.
A figura 4.11 exibe o resultado em um emulador HVGAde mdia densidade, e as
seguintes mensagens podem ser observadas no LogCat:
INFO/livroandroid: Densidade da tela: 1.0
INF O/livroandroid: 320x 480 dp igual a 320x 480 pix els
i
!
:
C


-
C
A
M
P
U
S

F
O
R
T
A
L
E
Z
A
]

B
I
B
L
I
O
T
E
C
A

|
154
Google An droi d para T ablets
A figura 4.12 exibe esse exemplo executando em um emulador w v g a de alta
densidade, e as seguintes mensagens podem ser observadas no LogCat:
INF O /livroandroid: Densidade da tela: 1.5
INF O /livroandroid: 320x480 dp igual a 480x 720 pixels
Figuras 4.11 e 4.12 - Exemplo de desenho executando em telas de mdia e alta densidade.
Neste ltimo exemplo o resultado igual nos dois emuladores, mesmo execu
tando em telas com diferentes tamanhos e densidades. Note que incrivelmente os
nmeros em dp, 320 x 480 pixels, foram convertidos para 480 x 720 pixels, que
exatamente o tamanho da tela do aparelho W VGA de alta densidade.
Perceba que o segredo do clculo recuperar a densidade do aparelho por
meio desse mtodo.
float densidade = r.getDisplay M etricsQ .density ;
A densidade da tela vai retornar 0.75 em celulares de baixa densidade, 1.0 em
celulares de mdia densidade e 1.5 em celulares de alta densidade. Esse valor o
fator pelo qual devemos multiplicar o valor em dp (density-independent pixel)
para obter o valor em pixels.
Portanto, vimos na prtica como criar cdigos que funcionam da mesma forma
em celulares com diferentes tamanhos e densidades de tela.
A densidade da tela vai retornar 0.75 em celulares de baixa densidade,
. 1.0 em celulares de mdia densidade e L5 em celulares de alta
densidade.
Capitulo 4 Trabalhando com diversos tamanhos de tela 155
6
4.5 Customizando as imagens conforme a densidade da tela
A pasta /res/drawable a famosa pasta onde salvamos nossas imagens, mas a partir
do Android 1.6, ao criarmos um projeto pelo Eclipse, automaticamente so criadas
quatro pastas:
/res/dmwable-ldpi: I magens utilizadas para telas de baixa densidade onde o
fator de converso 0.75. _
/ res/drawable-mdpi: I magens utilizadas para telas de mdia densidade (telas
conhecidas como sendo o padro at o Android 1.5).
/res/drawable-hdpi: I magens utilizadas para telas de alta densidade, onde o
fator de converso 1.5.
/res/dmwable-xdpi-. I magens utilizadas para telas de alta densidade, onde o
fator de converso 2.0.
Portanto, o padro colocarmos as imagens na pasta de densidade normal,
como a /res/drawable-mdpi, e customizarmos nossas imagens conforme o necessrio
para as demais densidades.
Por exemplo, digamos que temos uma imagem de fundo para as telas da
aplicao. Como uma tela H VGA padro de mdia densidade possui a largura de
320 pixels, essa imagem pode ter essa largura e uma altura qualquer, conforme
a necessidade. Sendo assim, digamos que a imagem tenha 320 x 480 pixels. Essa
imagem deve ser colocada na pasta /res/drawable-mdpi.
Ao executar a aplicao em celulares com uma tela de tamanho normal e de
mdia densidade, o exemplo vai funcionar perfeitamente. Mas ao executar a mesma
aplicao em um celular de alta densidade, o Android vai automaticamente procurar
as imagens na pasta /res/drawable-hdpi. Nesse caso, se a imagem no for encontrada
nessa pasta, a pasta padro /res/drawable-mdpi ser utilizada. Mas o problema que se
no customizarmos a imagem para outras densidades, o Android vai redimension-la
em tempo de execuo para se ajustar ao tamanho de tela, e neste caso a imagem
vai perder a qualidade.
A regra simples: como em celulares de alta densidade temos mais pixels na
tela, consequentemente podemos exibir imagens maiores com um nmero maior
de pixels para obter uma melhor definio e qualidade. Mas como converter a
imagem? Qual escala utilizar?
Nesse caso, basta multiplicar o tamanho da imagem normal por 1.5, que o
fator de converso de uma tela de alta densidade, lembra-se? Dessa forma, as
imagens de alta densidade sero 150% maiores que a imagem padro. Portan
to, 320 x 480 multiplicado por 1.5 resulta em 480 x 720, e esse o tamanho da
156 Google An droi d para T ablets
imagem que devemos inserir na pasta /res/drawable-hdpi. Se no fizermos isso em
tempo de compilao, o Android vai acabar fazendo do mesmo jeito em tempo
de execuo, e dependendo do caso, se tivermos muitas imagens, podemos ter at
problemas de performance.
Seguindo o mesmo raciocnio, o valor multiplicador para as telas de baixa
densidade 0.75, e dessa forma podemos multiplicar o tamanho da imagem por
esse valor para obter imagens na escala de 75% da original, para coloc-las na
pasta /res/drawable-ldpi.
Agora, se voc possui uma nica imagem e deseja que ela no seja escalada
para atender aos mais diversos tamanhos de tela, possvel utilizar o qualificador
nodpi, criando uma pasta hes/drawable-nodpi. Dessa forma o sistema deixaria essa
imagem do jeito que est.
Sempre que for necessrio a customizao de imagens, recomendado
que os qualificadores de densidade sejam utilizados (ldpi, mdpi,
hdpi, xhdpi). Tente evitar qualificar imagens pelo tamanho de tela
como fizemos nos arquivos de layout...
Se uni aparelho de alta densidade hdpi' no encontrar a imagem na
pasta / res/drawable-hdpi, ele vai buscar a imagem padro na pasta
/res/drawable-mdpi e vai redimension-la, perdendo a qualidade e
definio da imagem. Assim, recomendado que se criem imagens
com uma melhor qualidade e resoluo e que estas sejam colocadas
na pasta /res/drawable-hdpi. -
Se voc possui uma nica imagem e deseja que ela no seja escalada
para atender aos mais diversos tamanhos de tela, possvel utilizar
o qualificador nodpi, criando ma pasta /res/drawable-nodpi. Dessa
forma o sistema deixaria essa imagem do jeito que est.
4.6 A tag <supports-screens> no AndroidManifest.xml
Existe uma configurao no arquivo AndroidManifest.xml que influencia direta
mente como as aplicaes so exibidas nos diversos aparelhos existentes e at
como o Android Market filtra essas aplicaes.
Por exemplo, digamos que nossa aplicao no foi customizada para funcio
nar em celulares de telas pequenas com baixa densidade. Nesse caso, podemos
informar no AndroidManifest.xml que a aplicao no suporta esse tipo de tela.
Consequentemente, em um aparelho de baixa densidade ela no ser listada no
Android Market, assim podemos evitar que a aplicao seja instalada em um
aparelho para o qual ela no foi devidamente customizada.
I
Cap tulo 4 T rabalhan do com di versos taman hos de tela
157
Para isso podemos configurar a tag < supports-screens> informando para cada
densidade de tela se a aplicao pode ou no ser instalada. Se algum parmetro
for false, o Android Market no vai exibir a aplicao nesse aparelho.
<supports-screens
android:resiz eable= [ " true" | false"]
android:smallScreens=[ ''true" | "false"]
android:normalScreens= [ " true" | "false"]
android:largeScreens= [ true" | "false"]
android:x largeScreens= [ " true" | "false"]
android:any Density = [ " true" | "false"]
/>
No caso que ci tamos anteri ormente podemos col ocar o atri buto
android:smallScreens= " false" para indicar que nossa aplicao no suporta essa den
sidade, assim ela nem sequer ser exibida pelo Android Market.
A seguir temos a explicao de cada parmetro.
Parmetro Descrio
android:resiz eable
Indica se a aplicao deve ser escalada para executar em telas
maiores como large e xlarge, como tablets. Se for false, a aplicao vai
funcionar em escala pequena, executando em somente uma pequena
parte da tela. At o Android 1.5, esse valor era false por padro, mas a
partir do Android 1.6 ele true. Portanto, se voc definir no manifest as
propriedades minSdk Version ou targetSdk Version para API Levei =4 ou
superior, esse valor ser true por padro.
android:smallScreens
Indica se sua aplicao foi customizada para executar em telas
pequenas de baixa densidade.
android:normalScreens
Indica se sua aplicao suporta telas de tamanho normal de mdia
densidade.
android:largeScreens
Indica se sua aplicao foi customizada para telas fisicamente maiores
e pode ser redimensionada. Portanto, se voc definir no manifest as
propriedades minSdk Version ou targetSdk Version para API Levei =4 ou
superior, esse valor ser true por padro. Se esse atributo for false,
a aplicao vai funcionar de modo reduzido, utilizando somente
uma pequena parte da tela, o que conhecido como modo de
compatibilidade.
android:x largeScreens
Idem configurao para telas grandes, mas desta vez para telas
extragrandes, que so encontradas em novos tablets. Essa configurao
foi adicionada no Android 23 (API Levei =9). Esse atributo false
por padro, a no ser que no manifest as propriedades minSdk Version ou
targetSdk Version estejam configuradas para API Levei =9 ou superior.
android:any Density
Indica se sua aplicao customizou os recursos corretamente para
diversas densidades de tela, como, por exemplo, as imagens nas
pastas /res/drawable-ldpi, /res/drawable-mdpi e /res/drawable-hdpi,
conforme vimos anteriormente. Portanto, se voc definir no manifest as
propriedades minSdk Version ou targetSdk Version para API Levei =4 ou
superior, esse valor ser true por padro. Anteriormente, at o Android
1.5, nem sequer nnhams essa possibilidade.
-i..
158
Google An droi d para T ablets
Essa lista de parmetros a base que todo desenvolvedor Android precisa
conhecer. medida em que novas verses do sistema operacional vo surgindo,
pode ser que novos atributos sejam criados, e vale a pena sempre verificar a do
cumentao oficial.
http://developer.androi d .com/gui de/topi cs/mani fest/supports-screens-element.html
Por exemplo, a partir do Android 3.2 foram criados estes trs novos parmetros:
< supports-screens
android:resiz eable= [ " true' | "false" ]
android:smallScreens= [ " true" | " false" ]
android:normalScreens= [ " true" | " false" ]
android:largeScreens= [ " true" | " false"]
android:x largeScreens= [ " true | " false"]
android:any Density = [ " true" | " false"]
android:req uiresSmallestW idthDp= " integer"
android:compatibleW idthLimitDp= ' ' integer
android:largestW idthLimitDp= " integer"
/>
A seguir temos a explicao de cada parmetro novo.
Parmetro Descrio
req uiresSmallestW idthDp
Informa em dp a largura mnima que os layouts da sua aplicao
suportam, e a aplicao pode ser instalada. Por exemplo,
smartphones comuns possuem uma largura mnima de 320dp,
tablets de 7 possuem 600dp e os novos tablets possuem uma
largura mnima de 720dp. Para restringir, por exemplo, que a
aplicao seja instalada somente em tablets de 7 ou superior,
podemos inserir o atributo android:req uiresSmallestW idthDp= " 600" no
AndroidM anifest.xml.
compatibleW idthLimitDp
Atributo utilizado para habilitar o modo de compatibilidade
da tela, se os layouts da sua aplicao no foram criados para
tablets. Por exemplo, se a aplicao possui apenas layouts
para smartphone, podemos ativar o modo de compatibilidade
adicionando o atributo android :compatibleW idthLimitDp= " 320" n
AndroidM anifest.x ml. Esse atributo liga o modo de compatibilidade
da tela, o que vai fazer a aplicao rodar sem escalar em uma
pequena parte da tela, mas vai oferecer a opo de desabilitar o
modo de compatibilidade e escalar o aplicativo para preencher a
tela inteira.
largestW idthLimitDp
Exatamente a mesma coisa que o atributo compatibleW idthLimitDp,
mas no permite ao usurio desadvar o modo de compatibilidade.
Essa notao de tamanhos em dp tambm pode ser utilizada para criar layouts
diferenciados a partir do Android 3.2.
Portanto, podemos criar layouts para smartphones e tablets da seguinte forma
(somente no Android 3.2 ou superior).
/res /layout/layout.xml: Pasta de layout para smartphones.
/res / layout-sw600dp/layout.xml: Pasta de layout para tablets de 7 ou superior.
Tambm podemos facilmente criar layouts diferenciados para tablets de 7 e
10 conforme demonstrado a seguir.
/res /layout/layout.xml. Pasta de layout para smartphones.
/res /layout-sw600dp /layout.xml: Pasta de layout para tablets de 7.
/res /layout-sw720dp /layout.xml'. Pasta de layout para tablets de 10.
Para maiores informaes consulte a documentao oficial.
http://developer.android.com/guide/topics/manifest/supports-screens-element.htffll
Lembre-se que esses novos atributos vo compilar e funcionar
apenas a partir do Android 3.2.
4.7 A ordem que o Android utiliza para buscar os prefixos
Este tpico apresenta um resumo das configuraes que vimos at agora, onde
podemos verificar cada uma delas separada por grupos, conforme a tabela 4.1.
Cap tulo 4 T rabalhan do com di versos taman hos de tela " 9
Tabela 4.1 - Ordem que o Android utiliza para buscar os prefixos.
Configurao Descrio
Idioma
Representa o idioma " locale" para o qual o recurso deve ser
disponibilizado, como, por exemplo, pt para portugus ou en para ingls.
Tamanho da tela
Este grupo separa os aparelhos por tamanho fsico de tela em small,
normal, large e xlarge.
Aspect ratio
Este grupo separa os parelhos pela proporo entre a altura e a largura
da tela, indicando se o celular considerado comprido na vertical ou
largo na horizontal. Neste grupo os aparelhos so categorizados como
notlong e long.
Orientao da tela
possvel customizar os recursos da aplicao conforme a orientao da
tela, onde port utilizado para a vertical e land para a horizontal.
Densidade de tela
Neste grupo os celulares so agrupados por densidade da tela com os
qualificadores ldpi, mdpi, hdpi, x hdpi e nodpi.
Existem outras configuraes importantes, mas aqui estamos vendo as prin
cipais delas. Para a lista completa possvel consultar a documentao oficial.
Mostramos aqui essa tabela resumida para alertar que, se for necessrio criar
uma pasta de recursos que mescle diversos tipos de configuraes, como, por
exemplo, idioma, tamanho 3e tela e orientao, essa pasta deve seguir uma ordem
na sua nomenclatura.
Por exemplo, para definir uma pasta de imagem para o idioma portugus, para
telas grandes na horizontal, poderamos criar uma pasta com o seguinte nome:
/res/drawable-land-large-pt
Mas o nome dessa pasta no seguiu a ordem definida na tabela anterior e por
isso no vai compilar. Seguindo a tabela, devemos primeiro definir o idioma,
depois o tamanho da tela e ento a orientao.
Dessa forma, o nome correto para essa pasta :
/res/drawable-pt-large-land
4.8 Mantendo a compatibilidade com Android 1.6 ou superior
Aplicaes que desejam manter a compatibilidade com o Android 1.6 ou superior
devem pelo menos declarar no AndroidMamfest.xml uma configurao assim:
< uses-sdk android:minSdkVersion="4'' />
< supports-screens
android:smallScreens= " false"
/>
Note que foi inserido um parmetro indicando que a aplicao no deve ser ins
talada em celulares de tela pequena, servindo apenas para efeitos de demonstrao.
E importante termos conhecimento de que ao definir a verso mnima do An
droid como API Levei = 4 (Android 1.6), os demais parmetros so true por padro,
dessa forma este cdigo reduzido igual a estea.
< supports-screens
android:smallScreens= false"
android:normalScreens= " true"
android: largeScreens= "true' '
android:resiz eable= true"
android:x largeScreens= " true"
android:any Density = " true"
/>
Mas mesmo que diversos parmetros sejam true por padro, bom sempre
escrev-los, pois isso facilita a leitura
160 Google An droi d para T ablets
Cap tulo 4 T rabalhan do com di versos taman hos de tela 161
4.9 Mantendo a compatibilidade com Android 1.5 ou superior
Aqui temos um problema, ateno!
Aplicaes que desejam manter a compatibilidade com o Android 1.5 ou superior
geralmente devem declarar no AndroidManifest.xml uma configurao assim:
< uses-sdk android:minSdk Version=" 3'' />
< supports-screens
android:smallScreens= " false"
android:normalScreens= " true"
android:largeScreens= true"
android:resiz eable= " true"
android:x largeScreens= " true
android:any Density = " true
/>
E importante termos conhecimento de que ao definir a verso mnima do
Android como a API Levei = 3 (Android 1.5), diversos parmetros so false por
padro, ento vamos configurar tudo para true. Assim podemos customizar as
imagens para cada densidade de tela e ainda assim executar a aplicao em ce
lulares com Android 1.5.
Mas podemos ter um problema aqui, porque o conceito de grupos por tamanho
de tela (small, normal, large, x large) e densidades (ldpi,mdpi,hdpi,x hdpi), por exemplo, s
existe a partir do Android 1.6.
Dessa forma, se definirmos pastas de recursos utilizando nomes como /res/
drawable-mdpi, /res/drawable-notlong, entre outros, estas no vo funcionar no
Android 1.5, pois este no conhece essa nomenclatura. Portanto, ao executar a
aplicao no Android 1.5 ele no vai conseguir ler os arquivos e imagens corre
tamente, podendo causar resultados inesperados e at travamentos na aplicao.
Para solucionar esse problema voc precisa adicionar as imagens na pasta
padro, por exemplo, /res/drawable. Sim, infelizmente precisa duplicar. Nesse caso
seria duplicar o que estiver na pasta /res/'drawable-mdpi para a pasta /res/drawable.
Se sua aplicao vai ou no suportar o Android 1.5 deciso do seu projeto.
Para lhe ajudar a medir a quantidade de aparelhos Android existentes no mercado
separados por plataforma podemos acessar uma pgina que exibe um interessante
grfico de pizza com a distribuio de todos os aparelhos.
Para isso podemos acessar o seguinte link disponvel no site de desenvolve
dores Android:
http://developer.android.com/resources/dashboard/platforrn-versions.html
162 Google An droi d para T ablets
A figura 4.13 exibe os dados do grfico baseados nos aparelhos que acessaram o
Android Market nas ltimas duas semanas. Como podemos verificar, o Android L5
ocupa uma pequena fatia do grfico, e o que muitos desenvolvedores fazem criar
suas aplicaes compatveis com Android 1.6 ou superior, para evitar dor de cabea.
y i i i *
Android 2.2 :
Android 2.3 - 2.3.2
Android 2.3.3 2.3.4
Android 3.0
V-Android 3.1
."V* Android 1.5
'-Android 1.6
Android 2.1
Figura 4.13 - Grfico que exibe os aparelhos que acessaram o Android M ark et nas ltimas
duas semanas.
Da mesma forma, podemos acessar o grfico que separa os aparelhos por ta
manho fsico e densidade de telas no seguinte link, conforme podemos visualizar
na figura 4.14.
http://developer.android.com/resources/dashboard/screens.html
Normal / !dpi
Normal .'mdpi
Small / tidpi
Xlarge I mdpi
Largo / mdpi
Normal / hdpi
Figura 4.14 -^Grfico que exibe os aparelhos que acessaram o Android M ark et nas ltimas
duas semanas.
4.10 Melhores prticas para a criao de telas
Para a criao de telas no Android devemos utilizar algumas boas prticas que
fazem toda a diferena.
Estamos seguindo essas boas prticas desde o incio do livro em nosso projeto,
e como essa lista clssica, no podemos deixar de fazer uma breve reviso aqui.
1. Use wrap_ content e match_ parent (fill_parent) para definir a largura e altura das
views e evite utilizar valores fixos.
2. Se for utilizar algum valor fixo, utilize sempre a notao em dp - density -
independent pix el, e nunca me pixels.
3. No utilize o AbsoluteLay out, pois ele trabalha com coordenadas x /y fixas na
tela e isso vai trazer resultados no desejados ao executar a aplicao em
aparelhos com diversos tamanhos de tela.
4. Customize os recursos da aplicao conforme vimos neste captulo para
obter o melhor resultado visual possvel para cada tipo de tela.
Esse ltimo item essencial para fornecer ao usurio uma experincia dife
renciada ao utilizar seu aplicativo, independente do tipo da tela de seu aparelho,
se grande ou pequena.
No prximo tpico vamos validar nossos conhecimentos e customizar a tela
do projeto dos carros para um tablet de 7, que possui uma tela grande catego
rizada como large.
4.11 Aplicativo dos carros customizado para tela grande
Depois de ler este captulo vamos voltar prtica e trabalhar novamente no
aplicativo dos carros.
Digamos que o seu cliente solicitou que voc customize o projeto para o Ga-
lax y Tab da Samsung, o qual possui uma tela grande de 7 com 1024 x 600 pixels.
Como requisito, temos que continuar exibindo a mesma tela quando o apa
relho est na vertical. Porm na horizontal, como a tela possui mais espao, o
objetivo exibir os detalhes do carro na mesma tela que a lista, para melhorar a
interatividade do usurio e aproveitar melhor o espao da tela.
A figura 4.15 exibe o esperado pelo cliente.
Cap tulo 4 T rabalhan do com di versos taman hos de tela ^ 163
164 Google An droi d para T ablets
.;; :. :V<::
Chevrolet Bel-Air
Tucker1948 j
O Tucker foi realmente uma in_.
Chevrolet Corvette ^
O Chevrolet corvette tambm-.
Chevrolet I mpala Coupe ^
O Impala foi lanado em 1958, ...
Cadillac Deville Convertlble
O Cadillac 1968 conversvel t..
Chevrolet Bel-AIr
Este Chevrolet 1955 apresenta ...
Cadillac Eldorado
Este Chevrolet 1955 apresenta o melhor em design
e execuo. Mais de 92.000 dlares foi investido
nessa mquina de alta qualidade, transformando-o
no que se v hoje, no incluindo o trabalho
meticuloso. Este um carro que lida e passeios de
cada bocado to bom quanto ele looks. Vem Junto
como cortesia um Gen IV Chevrolet 620 cavalos de
potncia, 496 cid Motor de Corrida de algodo
acoplado a uma Engenharia KeislerTremec
TK0600 5-velocidade transmisso manual.
Figitm 4.15 - Tela customizada na horizontal para o Galax y Tab.
4.12 Instalando um add-on para simular o GalaxyTab
Como o requisito do projeto customizar o layout da aplicao para o Samsung
Galax y Tab, vamos precisar criar um emulador com as mesmas configuraes do
aparelho real ou, claro, preferencialmente ter o aparelho para testes.
Aqui vamos demonstrar como instalar um add-on especfico para o Galax y Tab, mas
note que os mesmos passos poderiam ser feitos tambm para outros aparelhos.
Para instalar o add-on, execute o gerenciador do SDK do A ndroid e instale o
Galax y Tab add-on disponvel no repositrios de terceiros dentro do grupo Samsung,
conforme a figura 4.16.
Depois de instalar o add-on, precisamos criar um emulador Android Virtual Device
para ele. Isso feito do mesmo mo do como se cria um emulador normal, mas no
combo onde voc seleciona a plataforma do Android desejada, como, por exemplo,
2.x ou 3.x, vai existir uma opo a mais, que o add-on que voc instalou, o qual pos
sui as configuraes exatas do Galax y Tab e vai nos ajudar muito no desenvolvimento.
Depois de criar a configurao podemos clicar em Details para visualizar os
detalhes desse emulador, conforme a figura 4.17. Nessa tela podemos visualizar os
detalhes do emulador criado, que simula o Galax y Tab real, e podemos verificar que
esse aparelho possui uma alta densidade, de 240 dpi. Portanto, podemos utilizar
os recursos na pasta hdpi, como /drawable-hdpi e /lay out/hdpi.
Cap tulo 4 T rabalhan do com di versos taman hos de tela 165
| X Andfoid SDK and AVD Manager - . .
Virtual de vices
SDK l o c a ^ l ^ ^ 1-vATcfawsVanfoi^^
mmmmM
4
i
Android Repostory ^
S ' D ThirdpMyAd-ons 1;
- 0 9 Googte Inc. C*-ssJ .Ooogte.com) [
4- 0 9 KYOCERA Corporbon (mw.echobykyocera.corri)
D d LGElactroncs(devdoper.lpnobfe.cofri) -
El9 SMnsungBedJ crteCraw8bor.samsungmcWe.com) p.
; B - 0 GAlSY T at^Mma dSSc^rAoc^AP I e, rwv*sbn ] p
Archivc for any OS p.
5 -O Son yEricsson (devetopef.sooyertc5sco.com)
8-Q 0K 1.1 by Sony Ertcsscn MoWe Cowiuifcdons AB, Android API 10, rcvfcTfr
?
f
l;
(
f;
l

,6
M
|
|lAddAdttnSI Wi.^ | P rDtepby updbtew-Refres
----IT-7- -- -----..
>* *. --
Figura 4.16 - Instalando Galaxy Tab Add-on da Samsung.
Figura 417 - Informaes sobre o emulador do GalaxyTab.
4.13 Customizando a tela para o GalaxyTab, tela = large
Antes de comear a leitura deste .tpico recomendado que se faa uma cpia do
projeto anterior LivroAndroid-Cap03-Carros e que o renomeie para LivroAndroid-Cap04-
-Carros-Galax y Tab, assim podemos customizar esse novo projeto e comparar com o
anterior, se necessrio.
166
Google An droi d para T ablets
Para customizar a tela para o Galax y Tab primeiramente precisamos conhecer
as suas configuraes. Esse aparelho possui uma tela grande, categorizada como
large, de alta densidade e pode utilizar recursos com o prefixo hdpi.
Mas para diferenciar esse tipo de aparelho de smartphones que tambm
possuem uma alta densidade hdpi vamos preferir faz-lo pelo tamanho fsico de
tela, sendo que o Galax y Tab se encaixa em telas grandes, categorizadas como large.
Como temos que criar um layout diferenciado somente na horizontal, vamos
adicionar um novo arquivo na pasta /res/lay out-large-land, conforme demonstrado
a seguir.
m /res/layout-large-lan d/carros.xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< linearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android:layout_width= "fill_ parent"
android: layout_ height= " fillj j arent"
android:gravity = " center"
android:back ground= " gcolor/fundo"
>
nclude lay out= @ lay out/include_ header" />
< LinearLay out
android:orientation= " horiz ontal"
android: layout_width=''fill_parent"
android: layout_height=" fill_parent"
>
<!-- Bloco Esq uerda -->
< Linearlay out
android:id= " @ + id/lay outLista"
android:lay out_ width= " 0dp"
android: layout_height="fill_parent"
android:padding= " 10dp"
android:orientation= " vertical"
android:lay out_ weight= " l"
>
< ListView
android:id= " @ + id/listview"
android :layout_width=''fill_parent"
android: lay out_height=''fill_parent"
android:cacheColorH int= " gcolor/transparente"
/>
< /LinearLay out>
<!-- Bloco Direita -->
< LinearLay out
android:id= " @ + id/lay outDetalhes"
android:lay out_ width= " 0dp
android: layout_ height= " fill_ parent"
android: padding=''10dp"
android:orientation= " vertical"
android:lay out_ weight= " l"
>
< ImageView
android: id="|9+id/imgDetalhes"
android:lay out_ width= " wrap_ content"
android:lay out_ height= wrap_ content"
android: lay out_ gravity = " center"
/>
< View
android: lay out_width= ' ' match_ parent"
android:lay out_ height= " ldp"
android:back ground= " # dddddd"
android:layout_marginTop= "10dp' '
android: lay out j narginBottom=' '10dp
/>
< ScrollView
android:lay out_ width= " match_ parent"
android: lay out_ height= " wrap_ content"
>
< Tex tView
android:id= @ tid/tDescDetalhes"
android: lay out_width="fill_ parent"
android:lay out_ height= " wrap_ content"
sty le= " @ sty le/tex tol4"
/>
< /5crollView>
< /LinearLay out>
< /LinearLay out>
< include lay out= " @ lay out/include_ footer/>
< /LinearLay out>
Se necessrio, podemos tambm utilizar os prefixos de tamanho de
tela e densidade juntos da seguinte maneira: /res/layout-large-hdpi e
/res/layoiit-large-hdpi-land.
Cap tulo 4 T rabalhan do com di versos taman hos de tela
168
Google An droi d para T ablets
Esse layout utiliza um LinearLayout na horizontal para dividir a tela em duas
partes, onde temos o Listview na esquerda com a lista decarros, e outro layout na
direita com os detalhes, que so a foto ampliada e a descrio.
Note que o layout que fica na direita recebeu o identificador @+id/ layoutDeta-
lhes para que a activity possa descobrir se esse layout existe ou no e atualize o
contedo automaticamente.
<!-- Bloco Direita -->
<LinearLayout
android:id=''@+id/ layoutDetalhes"
Esse layout ser utilizado apenas na horizontal, sendo que na vertical o compor
tamento antigo deve continuar, e os detalhes do carro sero exibidos em outra tela.
Agora vem a parte chata, e vamos duplicar um pouco de cdigo. Como faze
mos para atualizar a imagem e os detalhes do carro no layout da direita? Para
isso teremos que copiar o cdigo de atualizao das views, que na verdade est na
prxima tela de detalhes, em outra classe.
Note que antes de atualizar os detalhes precisamos testar se existe o layout
(3+id/ layoutDetalhes. Nesse caso a atualizao feita na mesma tela, caso contrrio
vamos abrir a tela de detalhes com a activity TelaDetalhesCarro normalmente.
(SOverride
public void onItemClick(AdapterView<?> parent, View view, int posicao,long id) {
Carro c =(Carro) parent.getAdapter().getItem(posicao);
View layoutDetalhes =findViewByld(R.id.layoutDetalhes);
if (layoutDetalhes != null) {
// Se horizontal, atualiza os detalhes na prpria tela
atualizarDetalhes(c);
} else {
/ / Se vertical, abre outra tela de detalhes
Intent intent =newIntent(this, TelaDetalhesCarro.class);
intent.putExtra(Carro.KEY, c);
startActivity(intent);
}
}
// Detalhes do carro
private void atualizarDetalhes (Carro carro) {
View layoutDetalhes =findViewById(R.id.layoutDetalhes);
if (layoutDetalhes != null) {
Log.i(TA6, "Exibindo carro: " +carro.nome);
TextView tHeader =(TextView) findViewByld(R.id.tHeader);
Cap tulo 4 T rabalhan do com di versos taman hos de tela
169
TextView tDescDetalhes = (TextView) findViewByld(R.id.tDescDetalhes);
if (tDescDetalhes != null) {
tgescDetalhes.setText(carro.desc);
}
if (tHeader != null) {
tHeader.setText(carro.nome);
}
// L a imagemdo cache
ImageView img = (ImageView) findViewByld(R.id.imgDetalhes);
CarrosApplication application = (CarrosApplication) getApplication();
DownloadlmagemUtil downloader =application.getDownloadImagemUtil();
Bitmap bitmap =downloader.getBitmap(carro.urlFoto);
if (img != null &&bitmap != null) {
img.setlmageBitmap(bitmap);
}
Note que no ListView j temos os identificadores g+id/ img e g+id/ tDesc, que so
declarados no layout / res/ layout-land/ carro_item, utilizado para exibir a linha. Des
sa forma, para diferenciar os identificadores a imagem e a descrio na parte
direita da tela esto com os identificadores (S+id/ imgDetalhes e | S+id/ tDescDetalhes. Se
no fizermos isso poderemos ter resultados inesperados ao utilizar o mtodo
findViewByid(viewld) para encontrar as views, uma vez que haveria duas com o mesmo
identificador na tela.
Depois disso vamos atualizar o mtodo atualizarView() para no somente po
pular o ListView com a lista de carros, mas tambm aproveitar e exibir os detalhes
do primeiro carro da lista, caso contrrio teramos uma pgina em branco ao
acessar o aplicativo pela primeira vez.
(SOverride
public void atualizarViewQ {
// Atualiza os carros na thread principal
if (carros != null && Icarros.isEmptyQ) {
listView.setAdapter(new CarroAdapter(this, carros));
Carro c =carros.get(0);
atualizarDetalhes(c);
}
}
)
170
Google An droi d para T ablets
Um ponto importante que precisa ser lembrado sobre a implementao
do mtodo onConfigurationChanged(config) caso seja o caso. Apenas para lembrar
este mtodo chamado quando colocamos a configurao android :configChanges =
" orientation" no AndroidManifest.xml, com o objetivo de tratar a troca de orientao
da tela manualmente, e evitar que o Android destrua a activity atual e crie uma
nova ao girar a tela.
Porm se essa configurao for adicionada, o desenvolvedor responsvel por
atualizar toda a tela novamente, e neste caso tambm vamos atualizar o layout
de detalhes se este existir na tela, no caso da horizontal.
(SOverride
public void onConfigurationChanged(Configuration newConfig) {
super. onConfigurationChanged(newConfig);
setContentView(R.lay out.carros);
listview = (Listview) findViewById(R.id.listview);
listview.setO nltemClick Listener(this);
if (carros != null 88 !carros.isEmpty ()) {
listview.setAdapter(new CarroAdapter(this, carros));
View lay outDetalhes = findViewById(R.id.lay outDetalhes);
if (lay outDetalhes != null) {
Carro c = carros.get(0);
atualiz arDetalhes(c);
}
}
}
A seguir podemos visualizar o cdigo-fonte atualizado da classe TelaListaCar
ros, que exibe uma tela customizada para o Galax y Tab na horizontal, melhorando,
e muito, o aproveitamento de espao. Lembre-se que esse layout ser utilizado
apenas em aparelhos que possuem uma tela categorizada como large, que o
caso do primeiro Galax y Tab da Samsung que escolhemos para este exemplo, uma
vez que criamos o arquivo /res/lay out-large-land/carros.x ml customizado para telas
grandes na horizontal.
) T el a Li staCa rros.ja va
public class TelaListaCarros ex tends LivroAndroidActivity implements O nltemClick Listener, Transacao
{
private static final String TAG = " livroandroid";
private Listview listview;
private List< Carro> carros;
private String tipo;
@ SuppressW arnings(" uncheck ed" )
Cap tulo 4 T rabalhan do com di versos taman hos de tela
171
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
tipo = getIntent().getStringEx tra(' ' tipo" );
listview = (ListView) findViewByld(R.id.listview);
listView.setO nltemClick Listener(this);
carros = (List< Carro> ) getLastNonCnfigurationInstance();
Log.i(TAG," Lendo estado: getLastNonConfigurationInstance()");
if (carros = = null 8& savedlnstanceState 1= null) {
// Recuperamos a lista de carros salva pelo onSavelnstanceState(bundle)
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
Log.i(TAG," Lendo estado: savedlnstanceState(carros)" );
this.carros = lista.carros;
}
if (carros != null) {
// Atualiz a o Listview diretamente
atualiz arViewQ ;
} else {
startTransacao(this);
>
}
(SOverride
public O bj ect onRetainNonConfigurationlnstanceQ {
Log.i(TAGj " Salvando Estado: onRetainNonConfigurationInstance()" );
return carros;
}
(SOverride
protected void onSaveInstanceState(Bundle outstate) {
super.onSavelnstanceState(outState);
Log.i(TAG," Salvando Estado: onSavelnstanceState(bundle));
// Salvar o estado da tela
outstate.putSerializ able(ListaCarros.K EY , new ListaCarros(carros));
}
@ O verride
public void executar() throws Ex ception {
// Busca os carros em uma thread
this.carros = CarroService.getCarros(this, tipo);
}
(SOverride
public void atualiz arViewQ {
// Atualiz a os carros na' thread principal
if (carros != null & & Icarros.isEmpty Q ) {
listview.setAdapter(new CarroAdapter(this, carros));
172
Google An droi d para T ablets
Carro c = carros.get(0);
atualiz arDetalhes(c);
}
}
gO verride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapter().getItem(posicao);
View lay outDetalhes = findViewByld(R.id.layoutDetalhes);
if (lay outDetalhes != null) {
// Se horiz ontal, atualiz a os detalhes na prpria tela
atualiz arDetalhes(c);
} else {
// Se vertical, abre outra tela de detalhes
Intent intent = new Intent(this, TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , c);
startActivity (intent);
}
}
// Detalhes do carro
private void atualiz arDetalhes (Carro carro) {
View lay outDetalhes = findViewByld(R.id.lay outDetalhes);
if (lay outDetalhes != null) {
Log.i(TAG, "Ex ibindo carro: " + carro.nome);
Tex tView tH eader = (TextView) findViewByld(R.id.tHeader);
Tex tView tDescDetalhes = (TextView) findViewByld(R.id.tDescDetalhes);
if (tDescDetalhes !- null) {
tDescDetalhes.setex t(carro.desc);
}
if (tH eader != null) {
tH eader.setTex t(carro.nome);
}
I I l i 3imagem do cache
ImageView img = (ImageView) findViewBy ld(R.id.imgDetalhes);
CarrosApplication applcation = (CarrosApplication) getApplicationQ ;
DownloadlmagemU til downloader = application.getDownloadImagemU til();
Bitmap bitmap = downloader.getBitmap(carro.urlF oto);
if (img != null 8& bitmap != null) {
img.s etlmageB itmap(bitmap >;
}
}
}
1
Cap tulo 4 T rabalhan do com di versos taman hos de tela
173
(SOverride
public void onConfigurationChanged(Configuration newConfig) {
super. onConfigurationChanged(newConfig);
setContentView(R.lay out.carros);
listview = (Listview) findViewByld(R.id.listview);
listview.setO nltemClick Listener(this);
if (carros 1= null & & Icarros.isEmpty O ) {
listview.setAdapter(new CarroAdapter(this, carros));
View lay outDetalhes = findViewBy ld(R.id.lay outDetalhes);
if (lay outDetalhes != null) {
Carro c = carros.get(0);
atualiz arDetalhes(c);
}
}
}
}
4.14 0 problema de duplicar o cdigo
Essa implementao para o GalaxyTab de 7 possui um problema grave enfren
tado em muitos sistemas: o cdigo est sendo duplicado.
Como vimos, foi duplicado o trecho que atualiza a foto do carro e sua descrio,
porque tivemos o requisito de exibir os detalhes do carro diretamente na mesma
tela da lista, se o aparelho estiver na horizontal.
Dessa forma, a classe TelaListaCarros possui o mesmo cdigo que a classe TelaDe-
talhesCarro estava utilizando, e isso nos trouxe um problema grave de manuteno
de cdigo. Agora, sempre que tivermos que atualizar a lista ser necessrio fazer
a mesma coisa em dois lugares.
A boa notcia que a partir do Android 3.0 Honeycomb um dos conceitos que
foram criados para auxiliar os desenvolvedores foi justamente uma API para criar
views em telas grandes e distribu-las na tela em forma de componentes reutilizveis.
Essa API de F ragments, onde um fragment basicamente uma view poderosa,
que possui um ciclo de vida bem definido e pode gerenciar o seu contedo como
se fosse uma activity Um fragment funciona como uma miniactivity dentro de
uma activity principal.
Mas isso assunto para o prximo captulo, onde vamos comear a estudar os
novos recursos disponveis a partir do Android 3.x para criarmos interfaces diferen
ciadas para tablets com o mximo de produtividade e reaproveitamento de cdigo.
CAPTULO 5
Android 3.x para tablets
"Honeycomb"
Vamos iniciar os estudos do Android 3.x criado para tablets, que recebeu o nome
de Honeycomb e permite ao usurio desfrutar de todo o espao disponvel na
tela, com uma navegao intuitiva e com diversas animaes, fornecendo uma
tima experincia ao navegar em um tablet.
Neste captulo vamos continuar o desenvolvimento da aplicao dos carros e
iniciar a migrao para tablets, verificando os detalhes de compilao do projeto
para que a aplicao fique compatvel com Android 3.x.
5.1 I ntroduo
Com a popularizao dos tablets e a grande busca dos usurios por esses equi
pamentos surgiu a necessidade de otimizar e customizar o Android para que se
usufrua ao mximo do tamanho de tela disponvel nesses aparelhos.
A resposta do Google para essa demanda foi o lanamento do Android 3.x,
conhecido como Honeycomb, com o objetivo de fornecer ao usurio uma tima
experincia ao utilizar os tablets.
A H ome do Android 3.x rica em contedo e possui uma interface leve e bem
fluida. Os recursos como Live W allpapers (papel de parede animado) e W idgets, que
so totalmente customizados, deixam a tela inicial do Honeycomb um espetculo,
conforme podemos visualizar na figura 5.1.
Um dos recursos mais famosos do Android o processamento multitarefa, e
o Honeycomb melhora ainda mais esse recurso, permitindo a visualizao de um
screenshot das telas que esto executando no momento.
174
Cap tulo 5 An droi d 3.x para tablets"Hon eycomb"
175
Figura 51 - Tela Home do Android 3.x Honeycomb.
Aplicaes como galeria de fotos, cmera e as nativas do Google, como, por
exemplo, Gmail, Y ouTube e Maps, possuem uma navegao diferenciada e foram
totalmente customizadas para atender s expectativas dos usurios ao utilizar
os tablets.
Afigura 5.2 exibe a aplicao do Gmail executando no Honeycomb, onde pode
mos ver claramente a barra de aes na parte superior, conhecida como ActionBar.
Podemos notar claramente que a interface foi bem distribuda para aproveitar
o espao disponvel na tela e foi criada utilizando a nova API de Fragments, que
vamos estudar no captulo 6, assim como a ActionBar, q ue veremos no captulo 7.
No existe muito que falar, pois a experincia de utilizar o tablet nica e
precisa ser sentida na prtica, pois apenas palavras e imagens no bastam para
expressar o que significa utilizar um tablet Android.
Este um livro de desenvolvimento. Sendo assim, vamos partir logo para a
prtica e iniciar a migrao do nosso aplicativo dos carros para os tablets com
Android 3.x.
Figura 5.2 - Gmail no Android 3.x.
5 . 2 Desenvolvendo aplicaes para o Android 3.x
O A ndroid 3.x, conhecido como Honeycomb, recebeu o cdigo de API Levei =
11, portanto este o nmero que precisamos conhecer para criar aplicaes fan
tsticas para tablets.
Nos prximos tpicos vamos descrever o bsico para migrar uma aplicao
desenvolvida para Android 1.6 ao 2.x para Android 3.x.
O primeiro passo abrir a ferramenta AVD Manager e fazer o download do
SDK Platform Android 3.x ou superior, como 3.1,3.2,3.x etc., conforme a figura 53.
5.3 Criando um Emulador para Android 3.x
A criao do emulador segue o mesmo padro dos demais, basta utilizar o utili
trio padro do SDK e selecionar o Android 3.x ou superior no combo conforme
a figura 5.4.
Mas utilizar o emulador do Android 3.x um grande desafio! Ele realmente
muito lento e necessrio ter muita pacincia para executar aplicaes de mdio/
grande porte e, naturalmente, necessria uma boa mquina para aguentar o
tranco. Se for possvel, melhor utilizar um dispositivo real.
Cap tulo 5 An droi d 3.x para tablets "Hon eycomb"
Virtual devicet
Avalable packages
SDKLocation: D:\J ava\Andro d\andro d-sd k-wind ows\
Installed packages
X Android SDKTools, revisionll
'Sj Android SDKPlatform-tools, revion 4
j| Oocumentation for Android SDVCAPI 12, revision1
H* SDK Platform Android 31, API 12,.revision 1
lWSDKPlatformAndroid 3.0, API 11, revision 1
fl1SDKPlatformAndroid 233, API 10, revision1
- Description--------------------;
Android SDK Platform 31, revision 1
De!ete~
| ggfrc>hj
Figura 5.3 - Instalador do SDK.
I f f i Cr e at e new Androi d Vi rt ual Devi ce(AVD) ' ' . j?!T|
Figura 5.4 - Criando um emulador para o Android 3.x.
5.4 Executando a aplicao dos carros no Android 3.x
Como estudo de caso para criarmos uma aplicao para tablets vamos utilizar a
mesma aplicao dos carros desenvolvida anteriormente neste livro.
Mas antes de fazermos qualquer alterao no projeto, vamos executar o apli
cativo dos carros do jeito que est atualmente, em um emulador ou tablet com
Android 3.x-de 10 e visualizar o resultado, que pode ser visto na figura 5.5.
Na tela de dashboard vemos que podemos utilizar melhor o espao disponvel,
mas o que chama mais a ateno o visual tanto da barra superior quanto da inferior.
178 Google An droi d para T ablets
&
Figura 5.5 - Dashboard no Android 3.x.
Na barra superior podemos verificar que no existe nenhum menu e botes
com aes, conforme vimos na figura 5.2, que exibe a aplicao nativa do Gmail.
Outro detalhe importante o quarto boto da esquerda para a direita na
barra inferior. Esse boto o menu de contexto do Android (menu fsico). Mas
no Android 3.x as aes do menu tradicional foram todas movidas para a parte
superior, onde fica a barra de aes conhecida como ActionBar.
S de verificar essas caractersticas j possvel deduzir que esse aplicativo
ainda no foi compilado corretamente para o Android 3.x. Dessa forma, nosso
objetivo neste momento habilitar a ActionBar e aproveitar melhor o espao dis
ponvel na tela.
Cap tulo 5 An droi d 3.x para tablets "Hon eycomb"
179
Para habilitar a ActionBar teremos que alterar o arquivo Androiimanifest.xml
para compilar o projeto corretamente, focando no Android 3.x (API Levei = 11).
E para aproveitar melhor o espao disponvel na tela vamos exibir a tela de sobro
ao lado do dashboard.
A figura 5.6 exibe o resultado que queremos ao executar a aplicao em um
tablet 3.x.
Note que nessa figura podemos verificar que a ActionBar est ativada na parte
superior, e na parte inferior o boto do menu no existe mais. Isso indica que a
aplicao foi corretamente compilada para Android 3.x, e um detalhe ao qual
ns, como desenvolvedores, precisamos estar atentos.
Carros 3.0
Google Android
flffpROID
Este rtvTO dedi cado j cs desenvol vedores Androi d que desej am apri morar
seus conheci mentos e est udar as novas f unci onal i dades dl sponrvet s no
Androi d 3 a como f ragments e aci i ont ur.

Figura 5.6 - Dashboard no Android 3.x depois de finalizada a migrao.


Vamos continuar e verificar as demais telas da aplicao.
A figura 5.7 exibe a tela com a lista dos carros do jeito que est, sem nenhuma
alterao no projeto.
Note que o nosso projeto j divide a tela em duas partes, pois customizamos a
tela de carros para o Galax y Tab de 7 que possui uma tela categorizada como large.
180
Google An droi d para T ablets
Nessa tela podemos perceber novamente que o quarto boto na parte inferior
(referente ao menu fsico) est ativado e que a barra de aes superior no est
ativada. Isso indica novamente que a aplicao no foi compilada para Android 3.x.
' Tuckef J 94* j i'
esoripnhi M Sj t Uxra.
CadillacEldorado
OOfi|u( CjdUbcBofaofeicertfmiaop)rjumsho6cj
Ferrari 2M GTO
A Fmri 250GTOumanomvdqw lei piedubdepc F*rrari 1H2-15Mnptcif>U
Ded(eChillenjef
OOuBflif** (ummsdlci tapenm dtduMptruidf Umnhe
ala pM^ce dn_
Ford Mumng 1978
OFordUjcaif i umkrtomw* KportNopfedundapfoid Mator Compi"!/ ]n<u(__
OTuder foi realmenteumaInovadonomundododesignautomvel,emboraomodelo1948
r foi onicomodelo] produadoseuefeitosobreomundodosawomvelsandapodeser
__ sentJdaathoje. Preaoo Tuder eAlexTremulisprojetouoTucker comoumatentativade
entrar naIndstriaautomotiva, eapesar deapenasumpunhadodecarrosForamproduzidosos
> recursosqueestavampresentesnaquelescaros eramextremamenteInovador paraapoca.

Figura 5.7 - Tela que lista carros no Android 3.x.


Tablets de 10 com Android 3.x geralmente possuem uma
tela categorizada com x large, mas como no captulo anterior
customizamos a tela para o GalaxyTab de 7 que possui uma tela
large, esse layout tambm utilizado pelo tablet de 10 de forma que
a aplicao logo de incio j divide a tela em duas partes.
Nosso objetivo para essa tela mais uma vez ser habilitar a ActionBar, para exibir
as aes no menu e tambm para usufruir dos novos recursos, como as Tabs, para
navegar entre os tipos clssicos, luxo e esportivos.
A figura 5.8 exibe o nosso projeto depois de finalizada a migrao para os
tablets com Android 3.x, sendo esse o nosso objetivo para os prximos captulos.
Lembre-se que, no Android 3.x, os itens de menu que antes eram
ativados pelo boto do menu fsico foram migrados para a ActionBar
e so exibidos na parte superior direita da tela.
Cap tulo 5 An droi d 3.x para tablets "Hon eycomb" 181
Figura 5.8 - Tela que lista carros depois de finalizada a migrao.
5.5 Preparando o projeto para Android 3.x
A ltima verso que temos do projeto dos carros foi feita no captulo 4, onde cus
tomizarmos a tela para o Galax y Tab de 7 e criamos a tela dividida em duas partes
quando o aparelho est na horizontal.
Para continuarmos a aplicao dos carros podemos fazer uma cpia desse
projeto e renomear de LivroAndroid-Cap04-Carros-Galax y Tab para LivroAndroid-Cap05-Carros-
-Android3. Feito isso, vamos aprender a compilar o projeto com Android 3.x.
Primeiramente vamos ressaltar que todas as aplicaes construdas para o
Android 2.x, se foram feitas corretamente, so compatveis com o Android 3.x. Isso
significa que a aplicao vai executar sem problemas, mas provavelmente ainda
no est otimizada para telas grandes. Essa otimizao pode ser feita com uma
compilao especfica para tablets, onde novos recursos sero ativados, e tambm
em conjunto com a criao de arquivos de layout customizado para telas grandes.
Precisamos estar atentos, porque possvel tornar as aplicaes compatveis
com o Android 3.x utilizando alguns pequenos truques.
A partir do Android 3.x um novo tema, chamado H olographic, foi criado para
fornecer ao usurio umajexperincia mvel diferenciada ao utilizav 0s tablets.
Esse novo tema no apenas altera as cores e textos da aplicao, mas tambm
adiciona funcionalidades importantes automaticamente, como, por exemplo, o
acesso barra de aes na parte superior.
No Android 3.x um novo tema, chamado de H olographic, foi criado
para fornecer ao usurio uma experincia mvel diferenciada ao
utilizar os tablets.
Ento vamos prosseguir, botar a mo na massa novamente e compilar o projeto
corretamente para Android 3.x.
Primeiramente vamos abrir o arquivo AndroidManifest.xml e verificar qual
a configurao para a verso mnima e alvo do projeto informada pelas tags
android:minSdk Version e android:targetSdk Version.
Geralmente, em aplicaes Android preenchemos apenas a tag android:minSdk Version
que especifica a verso mnima do SDK suportada. Portanto, para que a aplicao
seja compatvel com Android 1.6 ou superior configuramos o AndroidManifest.xml
da seguinte forma:
< uses-sdk android:minSdkVersion= "4' ' />
Dessa forma, como sabemos que a API Levei do A ndroid 3.0 11, podemos
simplesmente alterar a configurao da seguinte maneira:
< uses-sdk android:minSdkVersion="ll'' />
Isso tudo do que precisamos fazer para ativar o tema H olographic, deixar a
aplicao compatvel com Android 3.x e utilizar os novos recursos presentes no
sistema operacional. Mas o problema que dessa maneira configuramos o Android
3.0 ou superior com API Levei = 11 como a verso mnima suportada, e portando
a aplicao no ser mais instalada em um smartphone com A ndroid 2.x.
Para resolver esse problema precisamos deixar o Android 1.6 com a p i Levei =4
como a verso mnima suportada e tambm o alvo da compilao para o Android
3.0 ou superior com a p i Levei = li.
Isso pode ser feito da seguinte forma:
< uses-sdk android:minSdk Version= " 4" android:targetSdk Version= " ll" />
Estamos utilizando o Android 3.0 (API Levei = i l ) como exemplo
aqui, at porque esta a base da verso que revolucionou o mundo
dos tablets. Mas recomendado compilar o projeto e^deixar o
atributo targetSdkVersion apontando sempre para a ltima verso
disponvel. Na poca em que este livro estava sendo escrito o
Android 4J (API Levei = 16) tinha recm sido lanado.. Portanto
a melhor opo seria compilar o projeto com esta verso, e usar o
atributo android:targetSdkVersion="16". -
182 Google An droi d para T ablets
Cap tulo 5 An droi d 3.x para tablets "Hon eycomb"
183
Portanto, vamos fazer essa simples alterao no projeto dos carros e executar
a aplicao em um emulador ou tablet com Android 3.x. Note que a aplicao
ainda pode ser compilada com Android 2.x, pois neste momento apenas alteramos
Oarquivo AndroidManifest.xml.
Lembre-se que nesse momento estamos trabalhando com o projeto
LivroAndroid-Cap05-Carros-Android3, que uma cpia do projeto
LivroAndroid-Cap04-Carros-Galax y Tab construdo no captulo 4. Mas
estamos compilando como alvo o Android 3.0 ou superior utilizando
a propriedade android:targetSdk Version= " ll" .
Mas ao executar a aplicao podemos ver que o resultado ainda no foi satis
fatrio, e a princpio nada aconteceu. Por qu?
O problema que em nosso projeto o tema foi customizado no AndroidManifest.
xml com a tag android:theme= " @ sty le/tema" .
Para refrescar a memria podemos visualizar a seguir a parte do arquivo que
faz isso.
(f e An droi dMan i f est.xml
<?xml version= 1.0" encoding^ utf-S' ' ? )
nanifest x mlns: android= http://schemas.android.com/apk /res/android"
pack age= " br.livroandroid.carros"
android:versionCode= ' T
android:versionName= "1.0">
< uses-sdk android:minSdkVersion='' 4" android:targetSdk Version= ' ' H " />
application android:name= " .CarrosApplication" android:icon= "(8drawable/icon"
android:label= " @ string/app_ name" android:theme= " @ sty le/tema" >
Para criar o tema customizado identificado pela chave @style/tema criamos o
arquivo Ireslvalueslcss.xml no projeto, onde este foi configurado para utilizar o tema
Theme.Light padro dos smartphones.
A seguir podemos verificar apenas uma parte desse arquivo, que declara o
tema customizado.
f e /res/values/css.xml
<resources>
<!-- Tema padro -->
<style name= " tema" parent= " @ android:sty le/Theme.Light" >
< item name= " android:windowNoTitle" > true< /item>
</style>
</resources>
184
Google An droi d para T ablets
Outro probl ema nesse arqui vo que confi guramos a propri edade
android:windowNoTitle para true, para esconder o ttulo da aplicao que exibido
junto com a barra de status do Android, alm de ser baseado no tema @ android: sty le/
Theme.Light do Android 2.x, e no no tema H olographic do Android 3.x.
Esconder o ttulo da aplicao em smartphones uma tcnica comum ao
desenvolver para Android 2.x, mas no podemos fazer isso no Android 3.x ou
superior, pois justamente nesse espao que fica a famosa ActionBar.
Portanto, temos um problema, e mesmo compilando a aplicao com
android:targetSdk Version= " ll" o tema H olographic no foi habilitado, porque a nossa
aplicao est definindo um tema customizado baseado no Theme. Light para smar
tphones e ainda est escondendo a ActionBar.
Tecnicamente falando, assim como existem os temas @ android: sty le/Theme. Light e
(Sandroid:sty le/Theme.Black para Android 2.x, existem os temas @ android:sty le/Theme.H olo e
gandroid:sty le/Theme.H olo.Light para Android 3.x, e o que precisamos fazer encontrar
uma forma de criar um novo tema customizado baseado no H olographic e utiliz-lo
apenas na verso para tablets, de forma que a aplicao para smartphones com
Android 2.x continue funcionando da mesma forma.
Como no queremos influenciar a aplicao que j est funcionando, vamos
sobrescrever a chave @ sty le/tema, criando outro arquivo de estilos utilizando a pasta
/res/values-vli. Repare que vamos utilizar o identificador vil no final da pasta, para
garantir que esse recurso ser utilizado somente no Android 3.0 ou superior, que
possui API Levei = 11.
i d / res/values-vll/css.xml
< resources>
< sty le name="tema'' parent= @ android:sty le/Theme.H olo>
< /sty le>
< /resources>
Se preferir, tambm possvel utilizar o tema @android:style/Theme.Holo.Light.
Assim como existem os temas android:sty le/Theme.Light e
android:sty le/Theme.Black para Android 2.x, existem os temas @
android:sty le/Theme.H olo e @ android:sty le/Theme.H olo.Light para
Android 3.x ou superior.
i /res/values-v11/css.xml
< resources>
< sty le name= " tema" parent= " @ android:sty le/Theme.H olo.Light">
< /style>
< /resources>
Nesse momento, ao compilar o projeto teremos um erro de compilao, porque
o tema gandroid:sty le/Theme.H olo foi explicitamente referenciado no cdigo e no
existe no Android 2.x, que estamos utilizando para compilar o projeto.
Lembre-se que nas propriedades do projeto possvel escolher a verso do
Android utilizada para compilar a aplicao. Portanto, para resolver o problema de
compilao vamos alterar a compilao do projeto para Android 3.x ou superior.
Como estamos referenciando manualmente o tema android:style/
Theme.H olo no arquivo /res/values-vll/css.xml, somos obrigados
a compilar o projeto com Android 3.x, alterando as propriedades
deste. Essa compilao especfica necessria porque as verses
anteriores do Android no conhecem esse tema, e nesse caso temos
um erro de compilao.
Depois de entrar nas propriedades do projeto e compil-lo com Android 3.x,
vamos prosseguir.
Como neste momento o recurso @ sty le/tema existe em ambos os arquivos /res/
values/css.x ml e /res/values-vll/css.x ml, este ltimo ser utilizado nos tablets 3.x, pois
o mais especfico.
Dessa forma, smartphones vo utilizar o tema Theme. light com a barra de ttulos
desligada, enquanto os tablets 3.x vo utilizar o novo tema H olographic, habilitando,
entre outras coisas, a ActionBar, para que posteriormente seja possvel customizar
nossa aplicao.
Feito isso, j podemos executar o projeto em um tablet 3.x, e a ActionBar
estar ativada.
Ao criar o arquivo /res/values-vll/css.xml garantimos que esse tema
customizado baseado no H olographic seja utilizado apenas se o
sistema operacional for Android 3.0 ou superior que possui a API
Levei = 1L
Lembre-sequeparaotemaHolographicpossaserhabilitandoprecisamos
alterar o AndroidManifest.xml para android:targetSdk Version= " ll" .
Cap tulo 5 An droi d 3.x para tablets"Hon eycomb" 185
Na prxima vez que executarmos nossa aplicao poderemos verificar que na
parte superior apareceu uma nova barra de aes, a to esperada ActionBar, conforme
a figura 5.9. Neste momento ela est com o fundo preto e o cone da aplicao na
parte superior esquerda.
Posteriormente vamos inserir as Tabs na ActionBar para navegar entre os
tipos clssicos, luxo e esportivos, assim como inserir outros itens de menu com
algumas aes.
186 ( Google An dro d para T ablets
Figura 5.9 - Tela do dashboard no Android 3.x com o tema H olographic habilitado.
5 . 6 Customizando a ActionBar para Android 3.x ou superior
Como na verso para smartphone, usamos a imagem @ drawable/shape_ header com
o fundo gradiente azul para customizar o arquivo de header.
O prximo passo customizar a imagem de fundo da ActionBar com aquela
imagem com gradiente azul @ drawable/shape_ header que j temos criada no projeto,
e para isso vamos alterar o tema da aplicao conforme demonstrado a seguir.
i si /res/values-v11/css.xml
< resources>
< sty le name= " tema" parent= " @ android:sty le/Thene.H olo.Light" >
< item name= " android:actionBarSty le" > @ sty le/CarrosActionBar< /iteni>
< /sty le>
< sty le name=" CarrosActionBar'' parent= " @ android: sty le/W idget. H olo, Light .ActionBar">
< item name= " android:back ground">@ drawable/shape_ header< /itera>
< /sty le>
< /resources>
Simples assim! A vantagem de se customizar o tema da aplicao que no
precisamos alterar nenhum cdigo no projeto.
Depois dessa alterao podemos executar novamente o projeto, e agora a Ac
tionBar est customizada com o fundo azul, conforme a figura 5.10.
Cap tulo 5 An droi d 3.x para tablets"Hon eycomb" 187
&
Esportivos
Figura 5.10 - ActionBar com fundo customizado para tablets.
5.7 I dentificando se um tablet com Android 3.x
Um mtodo utilitrio que muito importante termos na manga descobrir se
estamos executando nossa aplicao em um smartphone ou tablet e, ainda, se
este um tablet com Android 3.x.
Lembre-se que podem existir tablets com Android 2.x.
Para isso vamos acrescentar alguns mtodos em nossa classe Androidutils que
criamos no projeto biblioteca LivroAndroid-AndroidU tils.
188
Google An droi d para T ablets
// Retorna se Android 3.0 ou superior (API Levei 11)
public static boolean isAndroid_ 3() {
int apiLevel = Build.VERSIO N.SDK _ INT;
if (apiLevel >= 11) {
return true;
}
return false;
}
Esse mtodo utiliza a constante Build.VERSIO N.SDK _ INT para verificar em tempo de
execuo qual a API Levei do aparelho. Se for API Levei = 11ou superior, sabemos
que no mnimo a verso do sistema operacional o Android 3.x Honeycomb.
Mas futuramente pdem existir smartphones que tambm tenham verses
do Android superiores ao A ndroid 3.x, como o novo Android 4.x, conhecido
como Ice Cream Sandwich, que vamos estudar no captulo 9, o qual tem como
objetivo trazer todas as revolues do Android 3.x, que inicialmente foi criado
especialmente para tablets, tambm para os smartphones e unificar a plataforma
de desenvolvimento do Android.
Portanto, tambm precisamos conhecer se o aparelho possui API Levei = 11
ou superior e se um tablet ou smartphone.
Como no aplicativo dos carros vamos inicialmente customizar as telas para
os tablets 3.x, os mtodos a seguir vo auxiliar a identificar os requisitos desse
aparelho.
// Retorna se a tela large ou x large
public static boolean isTablet(Contex t contex t) {
return (contex t.getResourcesQ .getConfigurationQ .screenLay out
& Configuration.SCREENLAY O U T_ SIZE_ M ASK )
>= Configuration. SCREENLAY O U T_ SIZE_ LARGE;
}
// Retona se um tablet com Android 3.x ou superior
public static boolean isAndroid_ 3_ Tablet(Contex t context) {
return isAndroid_ 3() 8& isTablet(contex t);
}
Lembre-se que podem existir tablets que no possuem o Android
3.x, assim como futuramente podem existir smartphones com
Android 4.x ou superior. O segredo sempre comparar se a API
Levei superior a 11.
A seguir podemos verificar o cdigo-fonte atualizado da classe Androidutils.
Cap tulo 5 An droi d 3.x para tablets "Hon eycomb"
189
# ) An droi dUti ls.java
public class AndroidU tils {
protected static final String TAG- = "livroandroiJ ";
public static boolean isNetwork Available(Contex t contex t) {
Connectivity M anager connectivity = (Connectivity M anager)
contex t.getSy stemService(Contex t.CO NNECTIVITY _ SERVICE);
if (connectivity == null) {
return false;
} else {
Network lnfo[ ] info = connectivity .getAllNetwork lnfoQ ;
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[ i] .getState() == Network lnfo.State.CO NNECTED) {
return true;
}
}
}
}
return false;
}
public static void alertDialog(final Contex t context, final int mensagem) {
try {
AlertDialog dialog = new AlertDialog.Builder(contex t).setTitle(
contex t.getString(R.string.app_ name)).setM essage(mensagem).create();
dialog.setButton(''OK'', new DialogInterface.O nClick Listener() {
public void onClick (DialogInterface dialog, int which) {
return;
}
});
dialog.show();
} catch (Ex ception e) {
Log.e(TAG, e.getM essage(), e);
}
}
public static void alertDialog(final Contex t context, final String mensagem) {
try {
AlertDialog dialog = new AlertDialog.Builder(contex t).setTitle(
contex t.getString(R.string.app_ name)).setM essage(mensagem).create();
dialog.setButton(OK", new DialogInterface.O nClick Listener() {
public void onClick (DialogInterface dialog, int which) {
return;
}
});
190
Google An droi d para T ablets
dialog.show();
} catch (Ex ception e) {
Log.e(TAG, e.getM essage(), e);
}
}
// Retorna se Android 3.x ou superior (API Levei 11)
public static boolean isAndroid_3() {
int apiLevel = Build.VERSIO N.SDK_ INT;
if (apiLevel >= 11) {
return true;
}
return false;
}
// Retorna se a tela large ou x large
public static boolean isTablet(Contex t contex t) {
return (contex t.getResources().getConfiguration().screenLay out
8 Configuration.SCREENLAY O U T_ SIZE_ M ASK )
>= Configuration,SCREENLAY O U T_ SIZE_ LARGE;
}
// Retona se ura tablet com Android 3.x ou superior
public static boolean isAndroid_ 3_ Tablet(Contex t contex t) {
return isAndroid_ 3() 88 isTablet(contex t);
}
// F echa o teclado virtual se aberto (view com foque)
public static boolean closeVirtualK ey board(Contex t contex t, View view) {
// F echa o teclado virtual
InputMethodManager iram = (InputHethodManager)context.getSysteniService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
boolean b = imni.hideSoftInputFroniWindow(view.getWindowTok en()j 0);
return b;
}
return false;
}
}
Dessa forma, quando for necessrio descobrir em tempo de execuo se estamos
executando nossa aplicao em um tablet com Android 3.x, basta usar esse mtodo.
// Verifica se tablet com Android 3.x
boolean android3 = AndroidU tils.isAndroid_ 3_ Tablet(contex t);
Agora vamos dar um exemplo prtico de onde poderamos utilizar esse mtodo.
Digamos que sua aplicao possui uma SplashScreen para exibir uma imagem
ou animao inicial para o usurio enquanto ela carrega, e depois disso voc
Cap tulo 5 An droi d 3.x para tablets "Hon eycomb"
191
precisa apresentar ao usurio a verso da sua aplicao otimizada para Android
2.x ou para tablets com Android 3.x.
Sem levar em considerao o cdigo para criar a splash, poderamos ter algo
mais ou menos assim para decidir se devemos abrir a aplicao na verso smar-
tphone 2.x ou tablets 3.x:
public class SplashScreen ex tends Activity {
. @ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out. splash);
// Verifica se tablet com Android 3.x
boolean android3 = AndnoidU tils.isAndroid_ 3_ Tablet(this);
if (android3) {
// Verso tablets 3.x ou superior
startActivity (new Intent(this, M ainTablets.class));
} else {
// Verso 2.x e smartphones 4.x
startActivity (new Intent(this, M ainSmartphone.class));
}
// Termina splash
finish();
}
}
Esse cdigo vai abrir a activity M ainSmartphone ou M ainTablets, dependendo do caso.
Essa apenas uma ideia de como criar dois aplicativos diferentes em um nico build.
Sabemos que podemos criar layouts diferenciados com os identificadores large,
x large etc., mas outra maneira de abrir uma aplicao totalmente diferente para
tablets essa que acabamos de mostrar. E amplamente utilizada, principalmente
ao migrar uma aplicao j existente do A ndroid 2.x para o Android 3.x, onde
podemos utilizar novas APIs.
Nos prximos captulos vamos estudar as novas APIs que existem a parti r do
Android 3.x, como Fragments e ActionBar, que vo auxiliar na tarefa de aprovei
tar o grande espao disponvel na tela e definir alguns padres visuais para as
aplicaes executando em tablets.
Na seqncia tambm vamos finalizar a migrao para Android 3.x, o famoso
Honeycomb, de forma que nossa aplicao possa apresentar um layout diferen
ciado para telas grandes,-dependendo do dispositivo que est sendo utilizado.
Honeycomb, l vamos ns!
CAPTULO 6
Fragments
Neste captulo vamos estudar a nova API - Fragments - que utilizada para
auxiliar o desenvolvimento de aplicaes para tablets e auxiliar a distribuir di
versas views pela tela, visando uma arquitetura simples, de fcil manuteno, e o
reaproveitamento de cdigo.
Primeiramente vamos estudar exerccios introdutrios sobre fragments, para
posteriormente migrar nosso aplicativo dos carros para o Android 3.x.
Tambm vamos verificar a biblioteca de compatibilidade disponibilizada para
os desenvolvedores, para que os novos recursos disponveis somente a partir do
Honeycomb possam ser utilizados em smartphones com o Android 2.x.
6.1 Introduo
Com a popularizao dos tablets e a grande busca dos usurios por esses equi
pamentos, surgiu a necessidade de otimizar e customizar o Android para que
se usufrua ao mximo do tamanho de tela disponvel nesses aparelhos, e neste
captulo vamos estudar a principal API que vai nos auxiliar a atingir esse objetivo.
Criar aplicaes para tablets uma arte, e vrios fatores precisam ser levados
em considerao. O mais importante de todos, claro, o tamanho da tela, que
deve ser aproveitada ao mximo. Outros fatores so, obviamente, caprichar na
interface de usurio e utilizar muitas imagens e animaes, que sempre encantam
os usurios.
Ao desenvolver para smartphones, geralmente temos uma tela simples, pois o
espao disponvel limitado. Dessa forma, o modelo tradicional de termos uma
activity com uma view, onde a activity controla toda a lgica da tela, sempre
atendeu s necessidades.
192
Cap tulo 6 Fragmen ts 193
Mas para desenvolver para tablets temos que usufruir de todo o espao dispo
nvel na tela, e muitas vezes necessrio preencher a tela com vrias views, cada
uma com um contedo diferente.
A figura 6.1 compara o modelo tradicional de uma aplicao em um smar-
tphone, onde duas telas precisam ser utilizadas para fazer a navegao de uma
listagem com uma tela de detalhes, e ao lado a mesma aplicao executando em
um tablet, onde uma nica tela pode ser utilizada para aproveitar ao mximo o
espao disponvel.
r = )
V c p i
Act i vi t y A Act i vi t y B Act i vi t y A com doi s f ragment s
Figura 6.1- Fmgment que divide a tela em pedaos.
Isso o que vai acontecer na prtica com nosso projeto dos carros, em que
na verso tablet vamos dividir a tela em duas partes. Na verdade j fizemos uma
implementao para o GalaxyTab de 7 mas na prxima vez vamos fazer essa
implementao da melhor forma possvel, utilizando Fragments, visando a faci
lidade de manuteno e o reaproveitamento de cdigo.
6.2 0 problema ao reutilizar views em diversas activities
Fora o espao disponvel que precisa ser bem aproveitado, algumas views podem
ser reutilizadas em vrias telas. Por exemplo, imagine uma aplicao qualquer
que tenha diversas telas, e nelas vrias views so exibidas, sendo que uma delas
se chama Anncio, que exibe uma propaganda qualquer. A figura 6.2 exibe uma
ideia dessa aplicao. Foram criados vrios layouts de tela para demonstrar, m-as
imagine que, independente de qual tela exibida, sempre necessrio inserir a
view do anncio.
Para implementar todas essas telas, naturalmente vamos precisar criar diver
sas activities. Cada activity vai criar a view correspondente da tela, e uma dessas
views ser o anncio.
194 Google An droi d para T ablets
Anncio aqui
-

_
:
Anncio aqui
Anncio aqui
Figura 6.2 - Aplicativo para tablets com uma view de anncios.
Mas como vamos adicionar a view de anncios em vrias activities, surge o
problema de que cada activity precisa controlar o anncio, buscar e atualizar o
seu contedo e tambm tratar os seus eventos. Consequentemente, vamos ter que
duplicar o cdigo do anncio em cada activity. claro que podemos criar algum
mtodo que centralize esse controle, para diminuir a duplicao de cdigo, mas
mesmo assim alguma coisa ser duplicada.
Para exemplificar teremos uma activity mais ou menos assim:
public class Telal extends Activity implements O nClick Listener {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.anuncio);
View view = findViewByld(R.id.anuncio);
view.setO nClick Listener(this);
// Buscar o contedo do an ncio utiliz ando uma thread...
}
@ 0verride
public void onClick (View view) {
// Tratar o clique no an ncio aqui, e outros eventos tamb m
>
}
Essa primeira activity naturalmente possui vrias views, cada uma com sua
lgica. E ela est inserindo o tratamento para o evento gerado pelo anncio.
Agora, na prxima tela, em outra activity, digamos que temos outro layout e
com outra lgica tambm, naturalmente. Dessa forma, na classe Tela2 teremos que
duplicar o cdigo que vai tratar o evento do anncio e, consequentemente, esto
comeando os nossos problemas.
public class Tela2 ex tends Activity implements O nClick Listener {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.anuncio);
View view = findViewBy Id(R.id.anuncio);
view.setO nClick Listener(this);
// Buscar o conte do do an ncio utiliz ando uma thread...
}
(JOverride
public void onClick (View view) {
// Tratar o cliq ue no an ncio aqui, e outros eventos tamb m
}
}
O anncio foi apenas um exemplo, mas em aplicaes para tablets comum
termos que reaproveitar as views em diversas telas e, portanto, acabamos dupli
cando o cdigo na activity. E claro que existem formas de deixar isso organizado,
mas no esse o caso.
Outra coisa bastante comum em aplicativos para tablets uma tela que uti
liza pequenas views distribudas de forma elegante para ocupar todo o espao
disponvel, como se fosse um portal na web, com diversas janelas para ocupar
todo o espao do site.
Cada uma dessas views precisa buscar o seu contedo de algum lugar, e fica
complexo demais para a activity controlar tudo isso sozinha.
Para solucionar esses problemas, a partir do Android 3.0 foi criada a API de
F ragments que vamos estudar neste captulo.
Cap tulo 6 Fragmen ts ^195
6.3 Como controlar o contedo de diversas views e diversas threads
em paralelo .
Uma conseqncia natural de termos mais espao disponvel na tela a utilizao
de vrias views menores para compor a tela e criar um layout diferenciado.
E para criar todas estas views, onde cada uma possui um diferente contedo,
algumas perguntas que ficam no ar so:
Como fazemos para fornecer o contedo para todas as views, se temos vrias
delas espalhadas pela tela, e cada uma com um diferente contedo?
Ser que temos que criar uma thread para cada view?
Mas como fazemos para controlar tantas threads e a atualizao de todas as
views, uma de forma independente da outra?
Como fica o cdigo da activity?
I sso traz uma complexidade para a activity, uma vez que agora ela deixa de
controlar uma nica thread para controlar vrias threads que podem estar execu
tando ao mesmo tempo, uma vez que para popular cada view recomendada
uma thread diferente, pois dessa forma o contedo de cada view atualizado ao
mesmo tempo e de forma independente das outras.
Se voc parar para pensar, esse um trabalhinho chato. No impossvel, mas
realmente ser necessrio queimar alguns neurnios para implementar algo que
fique bom e reutilizvel.
Esse foi um dos problemas que surgiram com os tablets, pois uma das con
seqncias de ter bastante espao disponvel na tela foi, naturalmente, essa tela
ficar cheia de views, e cada uma com um contedo diferente.
Mas agora a boa notcia! A partir do Android 3.0 foi criada a API de F ragments
para auxiliar nessa tarefa de criar uma tela cheia de informaes e views diferentes.
Um fragment permite desvincular a responsabilidade de gerenciar o conte
do da activity, de forma que a activity principal pode ficar bem simples. Cada
fragment representa uma view, e na verdade ele adicionado na tela da mesma
forma que uma view comum.
Podemos definir um fragment como uma view poderosa que capaz de
gerenciar o seu contedo sozinha e at mesmo abrir uma thread para buscar as
informaes na internet, se necessrio. Na verdade, um fragment acaba sendo
uma miniactivity, pois cada um possui uma view (por padro) e possui um ciclo
196 Google An droi d para T ablets
Cap tulo 6 Fragmen ts
197
de vida bem definido, como o de uma activity com os mtodos onCreate(), onPause(),
onStop() etc., e capaz de gerenciar seu contedo por conta prpria, alm de tratar
todos os eventos gerados pela sua view.
Dessa forma, a activity principal, ao invs de adicionar diversas views e ser
responsvel por gerenciar os seus contedos, pode utilizar no lugar dessas
views vrios fragments, que na prtica vo representar as mesmas views, mas
eles vo automatizar toda a tarefa e deixar a activity tranqila.
A activity final ento ficar simples, assim como a lgica de negcios em cada
fragment. Literalmente, ao utilizar fragments, vamos quebrar o cdigo em pedaos
menores e reutilizveis.
Outra vantagem de se utilizar fragments a possibilidade de reutilizar esse
componente em diversas telas.
Depois dessa introduo, vamos iniciar com a parte prtica, pois muitas vezes
um projeto de exemplo fala mais que mil palavras.
6.4 Criando o projeto com Android 3.x
Neste tpico vamos criar um projeto bem simples para explicar os fragments.
Para iniciar este captulo vamos criar um projeto com os seguintes dados.
Nome do projeto: LivroAndroid-Cap06-F ragments
Verso: Android 3.x
Pacote: br.Iivroandroid.cap06.fragments
Criar activity classe = Main.java
Verso mnima do SDK: 11(Android 3.0 ou superior)
Depois de termos criado o projeto, vamos construir uma simples tela, conforme
podemos visualizar na figura 63.
Um fragment permite desvincular a responsabilidade de gerenciar ^ .
contedo da activity, de forma que a activity principal pode ficar bem V.
simples. Cada fragment representa uma view na tela, e na verdade '
ele adicionado na tela da mesma forma que uma view comum.
Um fragment uma miniactvity que tem sua prpria view e
responsvel por controlar o seu contedo e tratar os eventos da tela.
198 Google An droi d para T ablets
UvroAndroid-Cap06-Fragments
Fragment! Texto Z
Figura 6.3 - Tela dividida em pedaos com fragments.
Antes de utilizarmos fragments, vamos primeiramente criar essa mesma tela
utilizando a maneira tradicional, ou seja, dividindo a tela em trs blocos princi
pais, com views.
Para isso vamos usar um LinearLay out horizontal, para separar a tela em duas
partes, esquerda e direita. Depois, no lado direito utilizaremos outro LinearLay out,
mas agora na vertical, para separar em duas partes, de cima e de baixo. Uma
propriedade interessante desse XML o android:lay out_ weight, que utilizado nos
layouts para atribuir um peso e, consequentemente, fazer com que as views fiquem
do mesmo tamanho e distribudas proporcionalmente na tela.
Ento mos obra e vamos alterar o arquivo de layout main.xml e criar essa
tela dividida em trs partes.
Sa /res/layout/mai n .xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= http ://schemas.android.com/apk /res/android"
android :orientation=''horizontal"
android: layout_widtti="fill_parent"
android: layout_ height="fill_parent"
android:back ground= " # ffffff
Cap tulo 6 Fragmen ts
<! -- Bloco 1 -->
cLinearLay out
android:id= " @ + id/lay outl"
android:orientation= verticar
android:lay out_ width= " 0dp"
android:layout_height="fill_parent"
android: background="# eeeeee''
android:lay out_ weight= -l"
android: layout:jnargin= "10dp"
android:gravity = " center"
>
< Tex tView
android:id= " g+ id/tex tl"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " Tex to 1"
android:tex tSiz e= " 18sp"
android:tex tSty le= " bold
android:tex tColor= "#000000"
/>
< /LinearLay out>
< LinearLay out
3ndroid:orientation= " vertical"
android:lay out_ width= " 0dp"
android:layout_height= "fill_ parent"
android:lay out_ weight= " 2"
>
<!-- Bloco 2 -->
< LinearLay out
android:id="@+ id/lay out2"
android:orientation= " vertical"
android:lay out_ width= fill_parent"
android:lay out_ height= " 0dp"
android:back ground= " # eeeeee"
android:lay out_ weight= l"
android:lay out_ margin= " 10dp"
android:gravity = center"
>
< Tex tView
android:id= " gtid/text2"
* android:lay out_ width= " wrap_ content"
android:lay out_ height="wrap_content'
android:tex t= Tex to 2"
android:lay out_ gravity = " center
200
Google An droi d para T ablets
android:tex t5iz e= " 18sp"
android:tex tSty le= bold"
android:tex tColor= " # 000000"
/>
< /LinearLay out>
<! -- Bloco 3 -->
< LinearLay out
android:id= " g+ id/lay out3
android:orientation= " vertical"
android: layout_ width= "fillj )arent"
android:lay out_ height= " 0dp
android:back ground= " # eeeeee"
android:lay out_ weight= " l"
android:lay out_ margin= " 10dp"
android: gravity=''center
>
< Tex tView
android:id= " @ + id/text3"
android:lay out_ width= " wrap_ content
android:lay out_ height= wrap_ content"
android:tex t= " Tex to 3"
android:lay out_ gravity = center"
android:tex tSiz e= " 18sp"
android:tex tSty le= bold"
android:tex tColor= " # 000000"
/>
</LinearLay out>
< /LinearLay out>
< /LinearLay out>
Note que no XML criamos trs TextViews, cada um com um android :id, como,
por exemplo:
< Tex tView android:id= " @ + id/tex tl" _ _ >
Depois de termos criado o layout, vamos voltar na activity e alterar os textos
de cada labei dinamicamente, para simular que as informaes esto sendo con
sultadas em algum lugar, como em um banco de dados ou na internet.
i ) Mai n .java
public class Main ex tends Activity {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
Cap tulo 6 Fragmen ts 201
setContentViewf R.lay out.main);
Tex tView tex tl = (Tex tView) findViewById(R.id.tex tl);
Tex tView tex t2 = (Tex tView) findViewById(R.id.text2);
Tex tView tex t3 = (Tex tView) findViewByld(R.id.textB)j
tex tl.setTex t(" Tex to 1.");
tex t2.setTex t(" Tex to 2.");
tex t3.setTex t(" Tex to 3.");
}
}
Depois de alterar o cdigo da activity para atualizar os textos dinamicamente
e executar o projeto, o resultado dever ser similar figura 6.4.
Figura 6.4 - Tela dividida em pedaos com TextView.
Agora vamos analisar essa implementao e pensar em algo um pouco mais
complexo.
E se as informaes para carregar os dados precisassem ser lidas de um banco
de dados? E se essas informaes precisassem ser buscadas de um web Service
ou algo assim?
Nesse caso o cdigo-fonte da activity vai ficar poludo, pois ser necessrio
realizar vrias chamadas para classes que acessam banco de dados, web services,
criar e controlar as threads etc. claro que podemos encapsular essas chamadas em
classes utilitrias, mas mesmo assim a activity ficar com o papel de controlar tudo.
!

:
C
E

-
C
A
M
P
U
S

F
O
R
T
A
L
E
Z
A

B
I
B
L
I
O
T
E
C
A
202
Google An droi d para T ablets
Para auxiliar nessa tarefa e tornar o desenvolvimento de aplicaes para tablets
mais produtivo foi criado a partir do Android 3.0 a API de fragments, que tem a
funo de permitir um maior poder e flexibilidade na criao de telas. No prxi
mo tpico vamos alterar este simples exemplo para utilizar fragments e tambm
estudar quais as vantagens de utilizar esse novo recurso.
6.5 Alterando o exemplo para utilizar fragments
Neste tpico vamos alterar o exemplo para utilizar a nova API de fragments. Um
fragment como se fosse uma miniactivity e tambm possui uma view e um ciclo
de vida bem definido.
A vantagem de se utilizar fragments que um fragment pode substituir qual
quer view na tela, mas o gerenciamento do contedo, o tratamento de eventos, a
utilizao de threads, a lgica de negcio etc. ficam dentro desse fragment.
Ento uma activity pode conter vrios fragments, e cada um, por sua natu
reza, pode abrir threads tranquilamente, e sem atrapalhar a execuo de outros
fragments, pois cada um executa de forma independente e responsvel apenas
pelo seu contedo.
Ento mos obra e vamos alterar o exemplo.
Primeiramente vamos criar uma nova activity chamada M ainF ragments, a qual vai
utilizar o arquivo de layout /res/layout/main Jragments.xml que vamos criar a seguir.
Note que na activity no ficar nenhuma regra de negcio, como as atualizaes
dos textos que fizemos no exemplo anterior, isso porque agora cada fragment
representar uma parte da tela e poder gerenciar o seu contedo independente
de tudo.
Vamos seguir essa abordagem de criar outra activity para deixar o exemplo
anterior inalterado para sua comparao futura.
i Mai n Fragmen ts.java
public class M ainF ragments ex tends Activity {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R. layout. main_fragraents);
}
Cap tulo 6 Fragmen ts
203
Ento vamos criar um arquivo de layout novo, que ainda vai dividir a tela em
trs partes, mas vamos utilizar fragments no lugar dos simples TextView na tela.
Por exemplo, no layout anterior tnhamos algo assim para o bloco 1:
<!-- Bloco 1 -->
< LinearLay out
android:id= " @ + id/lay outl"
>
< Tex tView
android:id= " @ + id/tex tl
/>
< /LinearLay out>
Agora vamos substituir o TextView pela tag < f ragment> , conforme demonstrado
a seguir.
<!-- Bloco 1 -->
< LinearLay out
android:id= " gtid/lay outl"
>
<!-- Frag 1 -->
<fragment class= " br.livroandroid.cap06.fragments.F ragmentl"
android:id= " @ + id/fragl"
android:layout_width= "fill_parent"
android :layout_fieight=fill_parent"
android:lay out_ margin= " 10dp"
/>
</Linearlay out>
Da maneira tradicional, com trsTextViews na tela, a activity fica responsvel
por atualizar todos eles. E lembre-se que nem sempre as telas sero to simples
assim. A vantagem de utilizar fragments que cada um pode criar a sua view e
gerenciar o contedo de forma independente dos outros.
A seguir podemos visualizar o novo arquivo de layout que utiliza fragments.
#1 /res/layout/mai n _f ragmen ts.xml
< ? xml version= " 1.0" encoding= utf-8"? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " horiz ontal
android:layout_ width= " fill_ parent"
android: lay out_ height="fill_parent" .
android:back ground= " # ffffff"
204
Google An droi d para T ablets
<! -- Bloco 1 -->
< Linearlay out
android:id= " @ + id/lay outl"
android:orientation= verticar'
android:lay out_ width= " 0dp"
android :layout_height=" fill_parent"
android:back ground= " # eeeeee"
android:lay out_ weight= " l"
android:lay out_ margin= " 10dp"
android:gravity = " center"
>
< !-- F rag 1 -->
< fragment class= " br.livroandroid.cap06.fragments.F ragmentl"
android:id= " @ + id/fragl
android:layout_width="fill_parent"
android: lay out_ height="fill_parent'
android:lay out_ margin= " 10dp"
/>
< /LinearLay out>
< LinearLay out
android:orientation= " vertical"
android:lay out_ width= " 0dp"
android: layout_height=" fill_parent"
android:lay out_ weight= " 2"
>
<!-- Bloco 2 -->
< LinearLay out
android:id='' @+id/layout2"
android:orientation= " vertical"
android:lay out_ wdth= " fill_ parent"
android: lay out_ height= " 0dp
android: background="# eeeeee''
android:lay out_ weight= " l"
android:lay out j iargin= " 10dp"
android:gravity = "center
>
<!-- F rag 2 -->
< fragment class= "br.livroandroid.cap06.fragments.Fragnient2''
android:id= " @ + id/frag2"
android: lay out_width=" fill_ parent"
android:layout_height="fill_parent"
/>
< /LinearLay out>
Cap tulo 6 Fragmen ts
205
<!-- Bloco 3 -->
< LinearLay out
android:id*" g+ id/lay out3"
android: orientation= " vertical"
android:lay out_width="fill_parent"
android: lay out_ height= " 0dp"
android: back ground= " # eeeeee"
android:lay out_ weight= " l"
android:lay out_ margin= " 10dp"
android:gravity = " center"
>
<!-- Frag 3 -->
< fragment class= " br.livroandroid. cap06. fragments. F ragment3
android:id= " @ + id/frag3"
android: layout_width= " fill_ parent
android: lay out_ height="fill_parent" />
< /LinearLay out>
< /LinearLay out>
</LinearLay out>
_ Acostume-se com o conceito de dividir a tela em blocos. uma
A . tcnica comum no desenvolvimento para tablets. Para preencher o
SSB contedo dos blocos podem ser utilizados fragments, que inclusive
podem ser reaproveitados em qualquer lugar da aplicao.
A tag <fragment> declara qual classe do tipo android. app. F ragment vai preencher esse
espao da tela, e basicamente o que a classe F ragment ter que fazer retornar uma
view para ocupar esse espao que lhe foi reservado.
Note que os atributos lay out_ width e lay outj ieight, que so utilizados para definir
a largura e altura das views, entre outros, so utilizados da mesma forma agora
com os fragments.
Na verdade voc pode pensar em um fragment tambm como uma view, mas
ele sozinho ser capaz de buscar o contedo necessrio para atualizar a sua view,
sem a ajuda da activity
Agora repare na declarao da tag < fragment> e como foi declarada a classe
b r .livroandroid.cap06.fragments.F ragmentl.
fragment class= " br.livroandroid.cap06.fragments.F ragmentl"
android:id= " g+ id/fragl"
android:lay out_ width= " wrap_ content"
android: lay out _height= 'fill_parent"
android:lay out_ weight= " l
android:lay out_ margin= " 10dp" />
Portanto, o prximo passo criarmos a classe br.livroandroid.cap06.fragments.
Fragmentl, que precisa ser uma subclasse de android.app.Fragment, e pelo menos im
plementar o mtodo onCreateView(.. .) para retornar a view que ser responsvel
por exibir o contedo no espao reservado ao fragment.
A seguir podemos visualizar o cdigo-fonte do nosso primeiro fragment.
i ) Fragmen tl . j a v a -
public class Fragmentl extends Fragment {
(SOverride
public View onCreateView(LayoutInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view =inflater.inflate(R.layout.fragmentl, null);
TextView text =(TextView) view.findViewByld(R.id.textl);
text.setText("Fraginent! Texto 1.");
return view;
}
}
O mtodo onCreateView(. ..) chamado durante o incio do ciclo de vida do
fragment e deve retornar a view que ser utilizada para preencher o espao da
tela que lhe foi reservado. Para isso o prprio mtodo recebe como parmetro um
objeto Viewlnflater para auxiliar na criao da view.
Depois que a view do fragment for criada, o Android vai substituir, em tempo
de execuo a tag <fragment> inserida no XML pela view retornada nesse mtodo.
A seguir podemos visualizar o arquivo XML de layout do primeiro fragment.
Note que o cdigo-fonte deste XML exatamente igual ao primeiro bloco do
LinearLayout, que arrancamos do layout principal ao refatorar a aplicao para
utilizar fragments.
i s) /res/layout/f ragmen t1.xml
<?xml version="1.0" encoding=utf-8"?>
<LinearLayout xnlns:android="http:/ / schemas.android.com/ apk/ res/ android"
android:orientation="vertical"
android: layout_width="fill_parent'
android: layout_height="fill jarent"
android:gravity="center
>
<TextView>
android: id=''@+id/ textl
android:layout_width="wrap_content"
android:layout_height="wrap_content
206 Google An droi d para T ablets
&
android:text=" Frag 1"
android:textSize="18sp"
android:textStyle=bold"
android:tex tColor="#000000"
/>
</LinearLayout>
Pronto! J implementamos nosso primeiro fragment.
Da mesma forma que uma activity implementa o mtodo onCreate(bundle) e
utiliza uma view para criar a interface com o mtodo setContentView(view), um
fragment tambm segue o mesmo raciocnio. Mas ele implementa o mtodo
onCreateView( inflater, Container, bundle), que precisa retomar a view.
O resto do cdigo igual ao de uma activity, e note que tambm utilizamos
o mtodo view.findViewByld(R.id.textl) para acessar uma view que estava dentro do
XML de layout, mas agora totalmente independente dos outros componentes
presentes na tela principal.
Para finalizarmos o exemplo vamos tambm implementar os outros dois
fragments do lado direito da tela.
Fragmen t2.java
public class Fragment2 extends Fragment {
(SOverride
public View onCreateView(LayoutInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment2, null);
TextView tex t = (TextView) view.findViewById(R.id.text2);
text.setText("Fragment! Texto 2.");
return view;
}
}
& /res/layout/f ragmen t2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout x mlns:android=" http://schemas.android.com/apk/res/android"
android:orientation= "vertical"
android:layout_width="fill_parent
android: layout_height="fill_parent"
android:gravity="center"
>
<TextView
android: id= @+id/text2''
Cap tulo 6 Fragmen ts . 207
208
Google An droi d para T ablets
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " F rag 2"
android:tex tSiz e= " 18sp"
android:tex tSty le= " bold"
android:tex tColor= " # 000000"
/>
< /LinearLay out>
& Fragmen t3.java
public class F ragment3 ex tends F ragment {
(SOverride
public View onCreateView(LayoutInf!ater inflater, ViewGroup Container, Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment3, null);
Tex tView tex t = (Tex tView) view.findViewById(R.id.text3);
tex t.setTex t(" F ragment! Tex to 3.);
return view;
}
}
#1 /res/layout/f ragmen t3.xml
< ? x ml version= " 1.0encoding= " utf-8" ? >
< LinearLay out x mlns:android= http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android: layout_width=" fill_parent"
android:lay out_height=" fill_parent"
android:gravity = " center
>
< Tex tView
android:id= " @ + id/tex t3"
android:lay out_ width= " wrap_ content"
android:lay out_ height= wrap_ content"
android:tex t= " F rag 3"
android:tex tSiz e= " 18sp"
android:tex tSty le= " bold"
android: tex tColor= " # 000000"
/>
< /LinearLay out>
Aproveite e crie tambm o quarto fragment, pois vamos utiliz-lo posterior
mente.
Cap tula 6 Fragmen ts 209
(Si Fragmen t4.java
public class F ragment4 ex tends F ragment {
gO verride
public View onCreateView(LayoutInflater inflater, ViewGroup Container, Bundle savedlnstanceState) {
View view = inflater.inflate(R.lay out.fragment4, null);
Tex tView tex t = (TextView) view.findViewById(R.id.text4);
tex t.setTex t(Fragment! Tex to 4.");
return view;
}
}
( d /res/layout/f ragmen t4.xml
< ? x ml version= " 1.0" encoding= utf-8" ?>
<LinearLay out x mlns:android= " http://schemas. android.com/spk /res/android"
android: orientation= ''vertical"
android: lay out_ width= fill _ parent"
android:lay out_height= "fill_ parent"
android:gravity = " center"
>
< Tex tView
android:id= " @ + id/tex t4"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= Frag 4"
android:tex tSiz e= 18sp"
android:tex tSty le= " bold"
android:tex tColor= " # 000000"
/>
</LinearLay out>
Utilizando fragments podemos fazer com que cada parte da tela seja atualizada
de forma independentemente, com um ciclo de vida separado da activity, e um
componente que pode ser reaproveitado em outras telas.
Note que para executar este exemplo precisamos executar a activitjTlainF rag-
ments que criamos, caso voc tenha seguido essa nomenclatura. Quando eu crio
esses exemplos costumo criar tambm um menu inicial na aplicao para listar
todas as activities principais.
Portanto, vamos criar a classe M ainM enu, declar-la como a activity principal do
projeto e deixar as demais activities com a declarao resumida sem o intent-filter
da seguinte forma:
210
Google An droi d para T ablets
i An droi dMan i f est.xml
<? xml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.com/apk /res/android"
pack age= " br.livroandroid.cap06.fragments"
android:versionCode= " l
android:versionName= 1.0'' >
< uses-sdk android:minSdk Version= " ll" />
< application android:icon= " @ drawable/icon android :label= " @ string/app_ name"
android:theme= " gandroid:sty le/Theme.H olo.Light" >
ctivity android:label= " @ string/app_ name" android:name= " .M ainM enu" >
<intent-filter >
< action android:name= android.intent.action.M AIN" />
ategory android:name= android.intent.category .LAU NCH ER" />
</intent-filter>
< /activity >
(activity android:name= " .M ain" />
< activity android:name= .MainF ragments" />
< activity android:name= " .M ainF ragmentsAPI" />
< /application>
< /manifest>
i Mai n Men u.java
public class M ainM enu ex tends ListActivity {
private static final String[ ] ops = new String[ ] { " Activity normal" ," F ragments XML",
" F ragments API", "Sair" };
fW verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
int lay out = android.R.lay out.simple_ list_ item_ l;
Array Adapter< String> adaptador = new Array Adapter< String> (this, lay out,ops);
this.setListAdapter(adaptador);
}
gO verride
protected void onListItemClick (ListView 1, View v, int position, long id) {
switch (position) {
case 0:
startActivity (new Intent(this, M ain.class));
break;
%
case 1:
startActivity (new Intent(this, M ainF ragments.class));
break;
case 2:
startActivity (new Intent(this, M ainF ragmentsAPI.class));
break;
default:
finishQ ;
}
}
}
Note que no arquivo AndroidManifest.xml declaramos as activities M ainM enu, M ain,
M ainF ragments e j tambm a prxima activity que vamos criar posteriormente, a
M ainF ragmentsAPI, a qual podemos criar e deixar com um cdigo vazio neste momento.
Depois de criarmos esse menu inicial conseguimos organizar melhor nosso
exemplo. Ento agora vamos prosseguir.
Ao executar o projeto e selecionar a activity M ainF ragments no menu podemos
visualizar o resultado conforme a figura 6.5.
Podemos verificar que o layout da tela continua o mesmo, apenas alteramos
o texto exibido em cada parte da tela.
Neste momento cada fragment est gerando uma view independentemente
dos outros e responsvel pelo seu contedo.
Cap tulo 6 Fragmen ts 211
Figura 6.5 - Exemplo simples de fragments.
Este exemplo demonstrou como relativamente simples migrar uma tela de
uma activity para um fragment. Caso existisse uma lgica mais apurada na acti
vity, esta tambm seria movida para dentro do fragment, de forma que a activity
ficasse cada vez mais limpa.
Para concluir vamos resumir algumas das vantagens de se utilizar fragments:
utilizar fragments deixa o cdigo da activity mais simples de manter;
um fragment uma miniactivity capaz de fornecer contedo totalmente
independente de outros fragments;
um fragment pode iniciar uma thread para buscar informaes, se necessrio;
melhora a organizao do cdigo;
permite reutilizar o mesmo fragment em outra activity, sem duplicao de
cdigo.
6.6 Ciclo de vida de um fragment
Entender o ciclo de vida de uma activity sempre foi um diferencial importante ao
desenvolver aplicativos no Android, e o mesmo se aplica aos fragments.
Na verdade, se voc j conhece o ciclo de vida de uma activity, como os m
todos onCreate(bundle), onStartQ , onResumeQ , onPauseQ , on S t o p Q e onDestroy Q , e ainda os
importantes mtodos utilizados para controlar o estado de uma activity, como o
onSavelnstanceState(bundle), O onRetainNonConfigurationInstance() e O getLastNonConfiguratio-
nlnstanceQ que foram explicados no captulo 3, ser bem simples de entender o
ciclo de vida de um fragment, pois ele segue o mesmo conceito.
Este livro assume que voc um desenvolvedor j acostumado com a arquitetu
ra do Android, sendo assim vamos focar somente no ciclo de vida dos fragments.
Mas apenas para relembrarmos, a figura 6.6 exibe o tradicional ciclo de vida de
uma activity. Esse diagrama est em ingls, pois da documentao oficial.
O ciclcrde vida de uma activity possui os tradicionais mtodos onCreateQ cha
mados uma nica vez quando ela iniciada, e os mtodos como onPa useQ e onStopQ
indicando que a activity ser interrompida e movida para segundo plano. Outro
mtodo tradicional o onDestroy Q , chamado uma nica vez ao destruir a activity
Seguindo esse mesmo princpio, o ciclo de vida dos fragments tambm pos
sui esses mesmos mtodos, e estes so amarrados com a activity que declarou o
fragment, que conhecida como host-activity
212 Google An droi d para T ablets
Cap tulo 6 Fragmen ts
213
Figura 6.6 - Ciclo de vida de uma activity.
Portanto, quando algum mtodo do ciclo de vida de uma activity for chamado,
como o onPauseQ , por exemplo, todos os fragments dessa activity tero os seus
mtodos o n P a u s e Q consequentemente chamados. Da mesma forma, quando o
mtodo onResumeQ da activity for chamado, o mtodo onResume() de cada fragment
tambm ser.
A figura 6.7 exibe lado a lado o ciclo de vida de um fragment comparado com
os estados de uma activity
214
(
Google An droi d para T ablets
Estado da Acti vi ty Mtodos dos Fragmen ts
Creat ed onAt t achO
onCreat eQ
T
onCreat eVIewO
*
onAct l vt t yCreat edO
St art ed onSt art Q
Resumed
onResumeQ
1
Paused onPauseO
<

St opped
onSt opQ

Dest royed onDest royVi ewQ


onDest royO
onDetachQ
Figura 6.7 - Ciclo de vida de um fragment comparado com a activity.
A maioria dos mtodos do ciclo de vida dos fragments um espelho dos mto
dos da activity, mas tambm h outros mtodos especficos que existem somente
nos fragments, conforme podemos visualizar na figura'6.8.
Observe a ordem em que esses mtodos so chamados, pois essa uma infor
mao muito importante durante o desenvolvimento.
Cap tulo 6 Fragmen ts 215
onAt t achQ
onCreat eQ
onCreat eVi ewO
onAcf i vrt yCi eat edO
onSt art Q

onResumeO
Usurio pressiona
o boto voltar ou
o f ragment
substitudo/removido
onPauseQ
0 fragment
onSt opQ |
executa
novamenti
estava na ba
00
pois
ck stack
onPest royVIewO
Figura 6.8 - Ciclo de vida de um fragment.
A tabela 6.1 exibe os principais mtodos especficos para os fragments. Vamos
dar uma ateno especial aos mtodos onCreateView() e onActivity CreatedQ , pois so
os mais importantes.
.................................................................................................................... II ......1|
216 Google Android para Tablets
Tabela 6.1 - Principais mtodos do ciclo de vida de um fragment
Mtodo
- * Descrio
onAttach(activity )
Este mtodo chamado logo depois do fragment ser associado com
a activity. Note que o importante deste mtodo que ele recebe como
parmetro a activity que contm o fragment.
onCreateView( inflater,
viewgroup, bundle)
Este o mtodo mais importante, e uma view deve ser retornada
para criar o layout que ser utilizado por este fragment. A activity vai
receber essa view e adicion-la no lugar reservado para o fragment.
Note que para auxiliar a criao da view um objeto Layoutlnflater
fornecido nos parmetros.
onActivity Created()
Este mtodo chamado logo aps o onCreateQ da activity ter sido
finalizado. Este pode ser um bom momento de disparo das transaes
para buscar os dados para compor a tela.
onDestroy View()
Oposto do onCreateView(inflater,viewgroup,bundle), este mtodo
chamado quando a view criada pelo fragment est sendo destruda.
onDetach()
Oposto do mtodo onAttach(activity ), este mtodo chamado quando
o fragment est sendo desassociado com a activity.
6.7 Exemplo para debugar o ciclo de vida de um fragment
Para facilitar o entendimento dos diagramas vamos criar duas classes com logs
nos principais mtodos do ciclo de vida, tanto da activity quanto de um frag
ment. Sendo assim, vamos criar duas classes, que so DebugActivity e DebugF ragment,
conforme demonstrado a seguir.
DebugActi vi ty.java
public class DebugActivity ex tends Activity {
private static final String TAG = "livroandroid";
(SOverride
protected void onCreate(Bundle savedlnstanceState) {
Log.d(TAG," DebugActivity .onCreate(): ' + getClass().getSimpleName());
super.onCreate(savedlnstanceState);
}
(SOverride
protected void onStart() {
Log.d(TAG," DebugActivity .onStartQ : " + getClass().getSimpleName());
super.onStart();
}
(SOverride
protected void onResumeQ {
Log.d(TAG," DebugActivity .onResume(): " + getClass().getSimpleNanie());
super.onResume();
} 9
Cap tulo 6 Fragmen ts 217
gO verride
protected void onPauseQ {
Log.d(TAG," DebugActivity .onPauseQ : " + getClassQ .getSimpleNameQ );
super.onPauseQ ;
}
gO verride
protected void onStopQ {
Log.d(TAG, " DebugActivity .onStopQ : " + getClassQ .getSimpleNameQ );
super.onStopQ ;
}
gO verride
protected void onDestroy Q {
Log.d(TAG," DebugActivity .onDestroy Q : " + getClassQ .getSimpleNameQ );
super.onDestroy Q ;
}
}
l DebugFragmen t.java
public class DebugF ragment ex tends F ragment {
private static final String TAG = "livroandroid";
gO verride
public void onAttach (Activity activity ) {
Log.d(TAG," \ t> DebugF ragment.onAttachQ : " + getClassQ .getSimpleNameQ );
super.onAttach(activity );
}
gO verride
public void onCreate(Bundle savedlnstanceState) {
Log.d(TAG," \ t> DebugF ragment.onCreate(): " + getClassQ .getSimpleNameQ );
super.onCreate(savedlnstanceState);
}
(SOverride
public View onCreateView(Lay outInflater inflater, ViewGroup Container,
Bundle savedlnstanceState) {
Log.d(TAG," \ t> DebugF ragment.onCreateView(): " + getClassQ .getSimpleNameQ );
return super.onCreateView(inflater, Container, savedlnstanceState);
}
(SOverride
public void onActivity Created(Bundle savedlnstanceState) {
Log.d(TAG," \ t> DebugF ragment.onActivity Created(): + getClassQ .getSimpleNameQ );
super. onActivity Created(savedlnstanceState);
}
(SOverride
public void onStartQ ' {
218
Google An droi d para T ablets
Log.d(TAG," \ t> DebugF ragment.onStart(): " + getClassQ .getSimpleNameQ );
supen.onStart();
}
gO verride
public void onResume() {
Log.d(TAG,' ' \ t> DebugF ragment.onResumeQ : " + getClass().getSimpleName());
super.onResumeQ ;
}
gO verride
public void onPauseQ {
Log.d(TAG," \ t> DebugF ragment.onPauseQ : " + getClassQ .getSimpleNameQ );
super.onPauseQ ;
}
gO verride
public void onStopQ {
Log.d(TAG," \ t> DebugF ragment.onStopQ : " + getClassQ .getSimpleNameQ );
super.onStopQ ;
}
gO verride
public void onDestroy ViewQ {
Log.d(TAG," \ t> DebugF ragment.onDestroy ViewQ : " + getClassQ .getSimpleNameQ );
super. onDestroy ViewQ ;
}
gO verride
public void onDestroy Q {
Log.d(TAG," \ t> DebugF ragment.onDestroy Q : " + getClassQ .getSimpleNameQ );
super.onDestroy Q ;
}
gO verride
public void onDetachQ {
Log.d(TAG,\ t> DebugF ragment.onDetachQ : " + getClassQ .getSimpleNameQ );
super.onDetachQ ;
}
}
Depois de criarmos essas duas classes de debug vamos alterar nossa activity
M ainF ragments e o fragment F ragmentl, para estender essas duas novas classes e passar
a logar todos os passos sobre o ciclo de vida da aplicao.
Portanto, vamos fazer a alterao nas duas classes da seguinte maneira:
public class M ainF ragments ex tends DebugActivity {
public class F ragmentl ex tends DebugF ragment implements O nClick Listener {
Cap tulo 6 Fragmen ts
219
Feito isso vamos executar novamente o projeto, e no LogCat ser possvel veri
ficar a seguinte sada:
(Note que o nome de nossa activity M ainF ragments, mas no confunda o nome.
Essa classe uma activity)
DebugActivity .onCreateQ : M ainF ragments
> DebugF ragment.onAttach(): F ragmentl
> DebugF ragment.onCreateQ : F ragmentl
DebugActivity .onStart(): M ainF ragments
> DebugF ragment.onActivity CreatedQ : F ragmentl
> DebugF ragment.onStartQ : F ragmentl
DebugActivity .onResumeQ : M ainF ragments
> DebugF ragment.onResumeQ : F ragmentl
Podemos verificar que ao entrar na aplicao os mtodo onCreate(), onStar tQ
e o n R e s u m e Q da activity foram chamados e, consequentemente, os mtodos
onAttach (activity ), onCreateQ , onActivity CreatedQ , onStartQ e onResumeQ foram chamados
no fragment.
Agora vamos testar o processo de pausa da aplicao. Para isso pressione o
boto H ome para voltar tela inicial do Android. Ao pressionar esse boto pode
mos verificar o seguinte log informando que os mtodos onPauseQ e on S t o p Q so
chamados tanto na activity quanto no fragment:
> DebugF ragment.onPauseQ : F ragmentl
DebugActivity .onPauseQ : M ainF ragments
> DebugF ragment.onStopQ : F ragmentl
DebugActivity .onStopQ : M ainF ragments
Depois disso pressione o boto ao lado do H ome para verificar as aplicaes
recentemente abertas e volte para o nosso aplicativo. Isso vai restaurar o estado
da tela e, consequentemente, podemos verificar que os mtodos onStartQ e onResu
m e Q so chamados na acdvity e no fragment, informando que a aplicao voltou
a executar.
Esses passos de pausa e reinicio de execuo so muito comuns quando o
usurio pressiona o boto H ome e depois volta para a aplicao, ou at mesmo
quando o usurio recebe uma ligao telefnica e a activity da ligao ocupa o
topo da pilha de activities, fazendo com que a aplicao entre em pausa, para
posteriormente ser restaurada, se o usurio voltar para ela.
DebugActivity .onStartQ : M ainF ragments *
> DebugF ragment.onStartQ : F ragmentl
DebugActivity .onResumeQ : M ainF ragments
> DebugF ragment.onResumeQ : F ragmentl
220
Google An droi d para T ablets
Ns j testamos o processo de criao das telas, a pausa e o reinicio, e agora
vamos testar o encerramento da aplicao, que acontece- quando o usurio pres
siona o boto voltar. Se fizermos isso, teremos os seguintes logs informando que
os mtodos onPauseQ , o n S t o p Q e onDestroy Q foram chamados em ambas a activity e
fragment:
(Note que os mtodos onDestroy ViewQ e o n D e t c h Q do fragment tambm foram
chamados no fragment.)
> DebugF ragment.onPauseQ : F ragmentl
DebugActivity .onPauseQ : M ainF ragments
> DebugF ragment.onStopQ : F rgmentl
DebugActivity .onStop(): M ainF ragments
> DebugF ragment.onDestroy ViewQ : F rgmentl
> DebugF ragment.onDestroy Q : F rgmentl
> DebugF ragment.onDetchQ : F rgmentl
DebugActivity .onDestroy Q : M ainF ragments
6.8 Buscar um fragment na tela
Muitas vezes necessrio que a activity ou qualquer fragment encontre outros
fragments para executar determinado mtodo ou atualizar algum contedo.
Para isso, da mesma forma que podemos encontrar uma view com o mtodo
findViewBy id(id), podemos tambm buscar um fragment utilizando os mtodos
findF ragmentBy ld(id) e findF ragmentBy Tag(tag) da classe F ragmentM anager.
Lembre-se que quando adicionamos um fragment via XML ns informamos
ao android :id que identifique esse fragment, conforme demonstrado a seguir.
<!-- F rag 2 -->
< fragment class= " br.livroandroid.cap06.fragments.F ragment2"
android: id= " @ + id/f rag2"
android:lay out_ width= " fill_ parent
android:lay out_ height= "fill_ parent"
/>
Portanto, em qualquer trecho da activity ou at mesmo dentro de outro frag
ment possvel encontrar o F ragment2 buscando pelo seu identificador da seguinte
maneira:
F ragmentM anager fm = getF ragmentM anagerQ ;
F ragment2 frag2 = (F ragment2) fm.findFragmentById(R.id.frag2);
frag2.5etTex to(" Tex to do F ragment 2 atualiz ado! " );
Cap tulo 6 Fragmen ts 221
Dessa forma, se a classe F ragment2 tiver um mtodo como o setTex to(string) que
demonstramos a seguir, podemos invoc-lo sem problemas.
Fragmen t2.java
public class F ragment2 ex tends F ragment {
gO verride
public View onCreateView(LayoutInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.lay out.fragment2j null);
Tex tView tex t = (Tex tView) view.findViewById(R.id.text2);
tex t.setTex t(" F ragment! Tex to 2.");
return view;
}
public void setTex to(String msg) {
View view = getView();
if (view 1= null) {
Tex tView tex t = (Tex tView) view.findViewById(R.id.tex t2);
tex t.setTex t(msg);
}
}
}
Da mesma forma que podemos encontrar uma view com o mtodo
findViewByld(id), podemos tambm buscar um fragment utilizando
os mtodos findF ragmentBy ld(id) e findF ragmentByTag(tag) da classe
FragmentM anager.
Para testar a busca do fragment vamos criar um simples exemplo e aproveitar
para adicionar um recurso especial disponvel para tablets no Android, que a
API de ActionBar.
A ActionBar um widget especial que substitui a famosa barra de status na
parte superior da tela e popularmente conhecida como barra de aes. No An
droid 3.x a ActionBar contm o cone do aplicativo na esquerda e itens de menu
na direita para realizar determinadas aes.
Na figura 6.9 podemos ver a aplicao do Gmail nativa dos tablets Android e
sua ActionBar customizada na parte superior.
Ento vamos alterar a nossa activity M ainF ragment que criamos anteriormente e
adicionar uma ao na ActionBar. Assim podemos testar o mtodo findF ragmentByid(id)
e buscar o F ragment2, para chamar o mtodo que atualiza o contedo do texto.
222 Google An droi d para T ablets
jtwhx^gmail.com
Family
)
)
;)
)
Figura 6.9 - ActionBar do Gmail.
Felizmente, extremamente simples adicionar um item na ActionBar, e pode
mos utilizar o tradicional mtodo onCreateO ptionsH enu(menu), utilizado para adicionar
os menus na aplicao. S que desta vez, ao criar um M enultem vamos tambm
indicar que ele deve ser exibido como uma ao na ActionBar, com a chamada
ao mtodo setShowAsAction(tipo) conforme demonstrado a seguir.
(Si Mai n Fragmen t.java
public class M ainF ragments ex tends DebugActivity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main_ fragments);
}
gO verride
public boolean onCreateO ptionsM enu(android.view.M enu menu) {
M enultem ml = menu.add(0, 0, 0, " Alterar Tex to do Frag 2");
ml.setShowAsAction(M enuItem.SH O W _ AS_ ACTIO N_ ALW AY S);
return super.onCreateO ptionsM enu(menu);
}
gO verride
public boolean onM enuItemSelected(int featureld, M enultem item) {
F ragmentM anager fm = getF ragmentM anager();
Cap tulo 6 Fragmen ts
i
223
switch (item.getltemldQ ) {
case 0:
// Demonstra o m todo "findFragmentByld"
F ragment2 frag2 = (F ragment2) fm.findF ragmentById(R.id.frag2);
frag2.setTex to(" Tex to Atualiz ado! " );
break;
}
return true;
}
}
A nica restrio desse mtodo que para ele ser encontrado necessrio
compilar o projeto com Android 3.0 ou superior, porque esse mtodo no existe
em verses anteriores.
Feito isso, podemos executar o projeto, e agora, na parte direita superior po
demos verificar nosso item de menu. Ao selecionar o item de menu na ActionBar
o F ragment2 ser buscado pelo seu identificador, e um mtodo ser chamado para
atualizar o texto dentro dele.
F ragmentM anager fm = getF ragmentM anagerQ ;
F ragment2 frag2 = (F ragment2) fm.findFragmentById(R.id.frag2);
frag2. atualiz arTex to (" Tex to Atualiz ado!");
A figura 6.10 exibe o texto do segundo fragment alterado.
Figura 610 - Exemplo do mtodo findF ragmentBy ld(id).
224
Google An droi d para T ablets
4
"rr ~ -.T V "
A ActionBar um widget especial que substitui firiosa barra
d^s tatus 'na parte superior da tela e popularmente conhecida
cmo barra de aes. No Android 3.x ;AcdonBar contri cone-
d aplicativo na esquerda e itens de menu n direita para realizar
determinadas aes. : .i, '
6.9 Criando os fragments dinamicamente com FragmentTransaction
At o momento vimos como adicionar fragments diretamente no layout XML
utilizando a tag < fragment> . Outra maneira de adicionar um fragment na tela
utilizando diretamente a API J ava em tempo de execuo.
Para isso vamos utilizar a classe F ragmentTransaction conforme demonstrado no
exemplo a seguir.
F ragmentl fragl = new Fragmentl();
F ragmentM anager fm = getF ragmentM anager();
F ragmentTransaction t = fm.beginTransaction();
t.add(R.id.lay outlj fragl, "fragl");
t.commit();
Aclasse F ragmentTransaction obtida chamando o mtodo beginTransactionQ do F rag
mentM anager. Depois que obtemos uma transao podemos chamar alguns mtodos,
como add, remove ou replace dessa transao, para adicionar, remover ou substituir
um fragment em determinado layout que existe na tela. No final, para indicar que
a operao deve ser efetivada basta chamar o mtodo commitQ de F ragmentTransaction.
Para demonstrar a utilizao da classe F ragmentTransaction vamos criar um novo
exemplo com uma activity separada e um novo layout XML. Mas desta vez vamos
deixar os layouts vazios, e estes sero preenchidos dinamicamente pela API.
Primeiramente vamos criar o arquivo /res/lay out/main_ fragments_ api.x ml conforme
demonstrado a seguir.
di / res/layout/mai n _f ragmen ts_api .xml
< ? xml version= " 1.0" encoding="utf-8''?>
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " horiz ontal"
android: layout_ width= "fill_ parent"
android:lay out_height=" fill_parent"
android:back ground= " # ffffff"
Cap tulo 6 Fragmen ts
<! -- Bloco 1 -->
< LinearLay out
android:id= " g+ id/lay outl"
android:orientation= " vertical"
android:lay out_ width= 0dp"
android: lay out_ height="fill_parent
android: back ground= #eeeeee''
android:lay out_ weight= " l"
android: lay out_ margin= " 10dp"
android:gravity = " center"
>
<! -- Frag1-->
< /LinearLay out>
< LinearLay out
android:orientation= " vertical"
android:lay out_ width= " 0dp"
android: lay out_height="fill j arent"
android:lay out_ weight= " 2"
>
< !-- Bloco 2 -->
< LinearLay out
android:id= " @ + id/lay out2
android:orientation= " vertical"
android: layout_width="fill_parent"
android:lay out_ height= 0dp"
android: back ground= " # eeeeee"
android:layout_ weight= " l"
android: lay outj nargin= 10dp"
android:gravity=" center''
>
< !-- Frag 2 -->
< /LinearLay out>
<! -- Bloco 3 -->
< LinearLay out
android: id="(8+id/layout3"
android:orientation= " vertical"
android :layout_width="fill_parent"
android :layout_height=''0dp"
android: back ground^ eeeeee
android:layout_ weight= " l"
android:lay outj nargin= 10dp"
android:gravity = " center
226
Google An droi d para T ablets
<! -- F rag 3 -->
< /LinearLay out>
< /LinearLay out>
</LinearLay out>
Se abrirmos esse XML no edicor visual, a pr-visualizao ir exibir um modelo
de tela parecido com a figura 6.11. Note que desta vez o layout definiu apenas o
template da tela e deixou os trs espaos vazios.
Figura 6.11 - Layout de tela vazio.
Note que no layout XML definimos os identificadores para cada bloco, como,
por exemplo, android:id= " @ + id/lay outr, android:id= " @ + id/lay out2 e android:id= " @ + id/
lay out3".
O identificador do layout utilizado como parmetro para indicar onde cada
fragment ser adicionado. A seguir vamos implementar outra activity para adicionar
os fragments dinamicamente.
i ) Mai n Fragmen tsAPI.java
public class M ainF ragmentsAPI ex tends DebugActivity {
gO verride *
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedInstance5tate);
setContentView(R.lay out.main_ fragments_ api);
Cap tulo 6 Fragmen ts
227
Fragmentl fragl = new F ragmentlQ ;
Fragment2 frag2 = new Fragment2();
FragraentB frag3 = new Fragment3();
// Adiciona os fragments
F ragmentM anager fm = getF ragmentM anager();
F ragmentTransaction t = fm.beginTransactionQ ;
t.add(R.id.lay outl, fragl, "fragl");
t.add(R.id.lay out2, frag2, " frag2);
t.add(R.id.lay out3, frag3, " frag3);
t.commit();
}
}
Para adicionar um novo fragment em algum layout dentro da tela deve-se
informar o identificador desse layout, a instncia de um objeto do tipo F ragment e
a tag, que um identificador opcional do fragment, para que depois seja possvel
encontr-lo utilizando o mtodo findF ragmentBy Tag(tag).
Se voc executar esse exemplo, ver uma tela exatamente igual ao exemplo
anterior, onde os fragments eram adicionados diretamente no XML com a tag
< fragment> . Portanto, o visual da tela continua o mesmo, mas adicionamos os frag
ments dinamicamente.
Note que o parmetro tag tem a mesma funo do atributo android:id que defi
nimos anteriormente. Na verdade, para criar um fragment precisamos informar
um android:id ou uma android:tag. Se voc estiver criando os fragments diretamente
no XML, pode utilizar a tag android :tag= " frag2" , por exemplo. Mas lembre-se de que
precisamos utilizar um android:id ou um android:tag para identificar cada fragment.
A vantagem de utilizar o android:id que depois podemos usar nossa amiga
classe R para nos auxiliar a encontrar os fragments. Mas para criar fragments
dinamicamente via API somos obrigados a passar o parmetro tag.
6.10 FragmentTransaction - mais alguns exemplos
Agora vamos criar mais alguns exemplos para remover e substituir um fragment
dinamicamente pela API, o que so casos muito comuns em projetos reais, de
pendendo da interatividade de sua aplicao.
Ento vamos alterar a classe anterior para adicionar dois itens de menu na
ActionBar, para criar algumas funes no nosso aplicativo.
228
Google An droi d para T ablets
A primeira ao vai remover o F ragment2, se ele existir na tela, ou, caso contrrio,
adicion-lo.
A segunda ao vai substituir o F ragment3 pelo F ragment4.
# i Mai n Fragmen tsAPI.java
public class M ainF ragmentsAPI ex tends DebugActivity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main_ fragments_ api);
F ragmentl fragl = new Fragmentl();
F ragment2 frag2 = new Fragment2()j
F ragment3 frag3 = new Fragment3();
// Adiciona os fragments
F ragmentH anager fm = getF ragmentM anager();
F ragmentlransaction t = fm.beginTransaction();
t.add(R.id.lay outl, fragl, "fragl");
t.add(R.id.lay out2, frag2, " frag2);
t.add(R.id.lay out3, frag3, "frag3");
t.commit();
}
(SOverride
public boolean onCreateO ptionsM enu(M enu menu) {
super.onCreateO ptionsM enu(menu);
M enultem ml = menu.add(0, 0, 0, " Remover/Adicionar Frag2" );
ml.setShowAsAction(M enuItem.SH O W _ AS_ ACTIO N_ ALW AY S);
M enultem m2 = menu.add(0, 1, 1, " Trocar o Frag 3 pelo 4");
m2.setShowAsAction(M enuItem.SH 0W _ AS_ ACTI0N_ ALW AY S);
return true;
}
@ 0verride
public boolean onM enuItemSelected(int featureld, M enultem item) {
F ragmentH anager fm = getF ragmentM anagerQ ;
F ragmentlransaction t = fm.beginTransaction();
switch (item.getltemldQ ) {
case 0:
// Demonstra m todo "findF ragmentByTag"
F ragment2 frag2 = (F ragment2) fm.findF ragmentByTag("frag2")j
if (frag2 != null) {
// Remove
t.remove(frag2);
} else {
Cap tulo 6 Fragmen ts 229
// Ou adiciona, se no ex iste
frag2 = new Fragment2();
t.add(R.id.lay out2, frag2, "frag2")j
}
t.commitO ;
break ;
case 1:
// Demonstra " F ragmentTransaction" com " addToBack Stack "
F ragment4 frag4 = new Fragment4();
t.replace(R.id.lay out3, frag4, frag4");
t . addToBack Stack ( " replaceF rag3F rag4" );
t.commit()j
break;
}
return true;
}
}
Execute esse exemplo e veja o comportamento.
Neste exemplo vamos dar uma ateno especial ao segundo caso, onde subs
titumos o F ragment3, que estava no bloco definido pelo R.id.lay out3, pelo F ragment4.
F ragment4 frag4 = new Fragment4();
t.replace(R.id.lay out3, frag4, "frag4");
t .addToBack Stack (" replaceF rag3F rag4" );
t.commitQ ;
O mtodo replace(lay out, frag, tag) recebe a instncia do novo fragment e o iden
tificador do layout de onde ele deve ser exibido. Chamar o mtodo replace(lay out,
frag, tag) tem o mesmo efeito de chamar o mtodo remove(frag) e, na seqncia, o
replace(lay out, frag, tag).
Note que tambm chamamos o mtodo opcional t.addToBack Stack (" replaceF rag3
F rag4) para que seja possvel desfazer essa operao com o boto voltar. como
se fosse possvel fazer um rollback dessa transao ao pressionar o boto voltar.
Nesse caso, o F ragment3 voltaria a ocupar o layout. Esse mtodo habilita o conceito
de back stack que vamos estudar no prximo tpico.
Uma restrio que existe com relao aos mtodos replace e remove
da FragmentTransaction que eles somente funcionam se os fragments
foram adicionados no layout pela APL Caso a tag <fragment>tenha
sido utilizada no XML, esses mtodos no vo funcionar.
230
1
Google An droi d para T ablets
6.11 Fragment back stack
Por padro, ao pressionar o boto voltar o Android vai destruir a activity atual,
eliminando-a da pilha de activities conhecida como activity stack. Se dentro da
activity forem inseridos fragments, como no caso do exemplo anterior, onde uti
lizamos a classe F ragmentTransaction, a activity e, consequentemente, todos os seus
fragments tambm sero destrudos com um nico toque no boto voltar.
Faa o teste e verifique.
Por padro, o boto voltar vai destruir a tela e o fragment atual de uma s
vez. Mas e se for necessrio que o boto voltar desfaa uma operao feita pelo
F ragmentTransaction?
Para isso podemos inserir cada transao dos fragments numa pilha de controle
para o boto voltar, chamada de back stack. Dessa forma, ao pressionar o boto
voltar o A ndroid vai primeiramente tentar desfazer a ltima transao efetuada
pela classe F ragmentTransaction, sendo necessrio outro toque no boto voltar para
encerrar a activity.
Vamos executar o exemplo anterior para demonstrarmos como funciona a
back stack dos fragments e pressionar o item de menu Trocar o Frag 3 pelo 4.
Depois de executar a transao que substitui o fragment 3 pelo fragment 4,
podemos desfazer a operao pressionando o boto voltar. Isso s possvel por
que habilitamos o mtodo addToBack Stack (" replaceF rag3F rag4" ) durante a transao.
Quando utilizamos a classe F ragmentTransaction para incluir, substituir ou remover
um fragment da tela, podemos inclu-lo ou no em uma pilha separada para o
controle da operao de voltar.
Por padro, ao pressionar o voltar os fragments que esto sendo exibidos na
tela dentro da activity sero destrudos.
6.12 Biblioteca de compatibilidade
A nova API de fragments auxilia muito a organizao do cdigo da activity, dele
gando a responsabilidade para os fragments, mas essa API existe apenas para o
A ndroid 3.x ou superior e no pode ser utilizada em aplicaes para Android 2.x.
Pensando nisso foi criada a biblioteca de compatibilidade, que consiste em um
arquivo jar que precisa ser adicionado ao projeto e contm a API de Fragments.
&
Portanto, mesmo em aplicaes para Android 2.x possvel usufruir desses
recursos quando necessrio, e isso pode auxiliar a criar componentes reutilizveis
que funcionem em smartphones com Android 2.x e tambm nos tablets com
Android 3.x.
Para instalar a biblioteca de compatibilidade basta executar o instalador do
SDK e baixar a verso mais atualizada do componente Android Compatibility Pack age.
A figura 6.12 exibe a biblioteca de compatibilidade instalada.
Cap tulo 6 Fragmen ts
WffMlWMIMSISBSKnBBBtRBS&^tzffix
Vtrtu^devw
Avfebb pckges
X Android SDK Tocfe, revisicn 11
ftAndrodSDKPlatforTn-tools, revisiortS
'5' SDK Platform And-d 3.0, API 11, revtsion 1
'SC* Plstfom 2.3.3, API 10, revisicn l
<SDK Ptatform Aodrod 2.2, API 8, reMskxi 2
5' SC* Ptotform Anfrcd 1.6, API 4, revtsion 3 \
Simples ftr SC* API ll.revtsicn I
feGooqfc API s byGoote I nc., Android API 11, revision 1
'Zf.Gocfc APIs by Goofe I nc., Android API 10, ravtsion 2 |
8#.<Soe^eAPI s by Googte Inc., AnAxd API 8, revision 2
t#.GALAXY Tb Addon by Samsing Qectrorics Co., Ud., Andod API 8, revision 1
S Google USB Driver packaje, revfeiort 4 j
1 lAndrotd Cofr<v^tly pacM<>, f evwn3 f l
:i
GofflfMtiMtyttarfes, revfcton3 c { f t . ' ' * ' * c' 1 * * - * ! e*ri;
i locaoo; DipwaVAntfrtdVandrotfyj^l l-wtndomVaridrold-sA t<indo^extras\arAoid\conipafaMty / '. i
;W3m\ Wet e... : A* * * ! ;
Figura 6.12 - Biblioteca de compatibilidade instalada.
Depois de instalar a biblioteca de compatibilidade podemos encontrar o ar
quivo android-support-v4.jar na seguinte pasta, que a lib que precisamos adicionar
no classpath do projeto para habilitar a API de fragments, mesmo em um projeto
com Android 2.x.
/android-sdk /ex tras/android/compatibility M
6.13 Migrando o projeto dos Fragments para utilizara biblioteca de
compatibilidade
Para validar a utilizao da biblioteca de compatibilidade vamos migrar o nosso
projeto de exemplos que utiliza fragments para utilizar a biblioteca, em vez da
API nativa de fragments. Para isso vamos criar uma cpia do projeto LivroAndroid-
Cap06-F ragments, cham-lo de LivroAndroid-Cap06-F ragments-Support e realizar todas as
modificaes nesse projeto.
232
Google An droi d para T ablets
A biblioteca de compatibilidade disponibilizada em um arquivo jar, depen
dendo da verso mnima do A ndroid que precisamos suportar. Por exemplo,
uma biblioteca compatvel com a API Levei 4 ou superior, e outra compatvel
com a API Levei 13 ou superior. A verso mnima indicada pelo qualificador
no nome do diretrio, como, por exemplo, /v4 e /vi3. Neste caso, como desejamos
criar aplicaes que funcionem desde o Android 1.6 at as verses atuais, vamos
escolher a verso v4 da biblioteca.
Para comprovar que a biblioteca de compatibilidade funciona vamos compilar
esse mesmo projeto do qual demonstramos os fragments utilizando o Android
2.x. Para isso vamos seguir os seguintes passos:
Primeiramente entre nas propriedades do projeto e compile-o com o Android
2.x.
Depois altere a verso mnima do projeto para < uses-sdk android:minSdk Version= " 4"
/>. Por padro, ao criar projetos para o Android 3.0 a verso mnima fica como 11.
Feitas essas duas alteraes, diversos erros de compilao iro aparecer no
projeto, sendo que as classes F ragment e o mtodo M enultem.setShowAsAction(tipo) no
existem no Android 2.x.
O prximo passo copiar o arquivo android-support-v4.jar que possui as classes
de suporte a fragments e que se encontram na seguinte pasta:
/android-sdk /ex tras/android/compatibility M
Vamos inserir o arquivo android.-su-pport-v4.jar no classpath do projeto. Para isso
recomendado criar uma pasta lib dentro do projeto e inserir l esse arquivo.
Depois basta clicar com o boto direito do mouse no arquivo e escolher a opo
Build Path > Add To Build Path.
Depois que a biblioteca foi adicionada no projeto podemos substituir a classe
android.app.F ragment, que no existe, pela classe android.support.v4.app.F ragment, dispo
nvel na biblioteca. Note que todas as classes da biblioteca pertencem ao pacote
android.support.v4.
Continuando, vamos ao passo prximo passo.
Altere todas as activities do projeto que utilizam fragments para herdar de
android.support.v4.app.F ragmentActivity .
Para auxili-lo, a figura 6.13 demonstra a alterao e exibe a comparao do
projeto anterior, que utiliza os fragments nativos com o Android 3.x (na esquer
da), com o novo projeto que utiliza a biblioteca de compatibilidade (na direita).
Cap tulo 6 Fragmen ts
233
Figura 6.13 -Alterando o exemplo para compilar com a classe F ragmentActivity .
Feito isso, altere todos os fragments para herdar da classe android. support. v 4 . app.
F ragment.
A figura 6.14 demonstra como fazer a alterao. Note que o nome do pacote
da classe F ragment foi alterado para que se utilize a biblioteca de compatibilidade.
Figura 6.14 - Alterando o exemplo para compilar com a F ragment da biblioteca de
compatibilidade.
Agora todos os lugares onde utilizamos o mtodo getF ragmentM anagerQ precisam
ser alterados para getSupportF ragmentH anagerQ . Esse mtodo retorna a classe android.
support.v4.app.F ragmentH anager. Afigura 6.15 demonstra essa alterao.
Pronto, se voc seguiu esses passos, todos os erros de compilao referentes
aos fragments foram solucionados. Mas ns ainda temos um problema, porque o
mtodo M enultem.setShowAsAction(tipo) no existe no Android 2.x, pois a ActionBar
somente existe a partir do Android 3.x.
Mas para criar um cdigo compatvel com ambos Android 2.x e Android
3.x podemos utilizar a classe android.support.v4.view.H enuCompat e o mtodo
setShowAsAction(menuItem, acao).
234
Google An droi d para T ablets
Figura 6.15 - Utilizando o mtodo getSupportF ragmentM anager().
Portanto, vamos utilizar o mtodo M enuCompat.setShowAsAction(menultem, acao) no
lugar de menultem.setShowAsAction(acao). Para isso vamos criar uma classe chamada
M enuItemCompat e utilizar suas constantes no lugar da M enultem.SH O W _ AS_ ACTIO N_ ALW AY S,
que no existe no Android 2.x. Como as constantes que precisamos no existem
na classe M enultem do Android 2.x, vamos criar a nossa prpria classe M enuItemCompat
demonstrada a seguir.
[s Men ultemCompat.java
public d a s s M enuItemCompat {
public static final int SH O W _ AS_ ACTIO N_ NEVER = 0;
public static final int SH O W _ AS_ ACTIO N_ IF _ RO O M = 1;
public static final int SH O W _ AS_ ACTIO N_ ALW AY S = 2;
public static final int SH O W _ AS_ ACTIO N_ W ITH _ TEX T = 4;
}
Essa classe ser utilizada em conjunto com a classe android.support.v4.view.M enu-
Compat para criar os menus.
Para facilitar o entendimento a figura 6.16 demonstra a alterao.
4=d*. BalnrragnencsoM.nd. Bobuglc: Ivlt, l 1!*0l" ""i OtmjlGClvltr 1
StVorrldo ESS SOvecrlde
PUHC.oia ftoWW sovodloot.o.ceStotol 1 30 l1Tld < I
op.r.ooCro.toIoovodlmtooooSt.to); SSS
oocConteooVlo. |R. I v u i i s a m , |ggj <* 1
evocria (KUovorrldo
,ilio hooleononCuOpcuiaIcu|lilcou.vu..Bnu ram) 1 | H I*11' onCt.MOptlo^fcnu|uarou.vl.mu M
oultoml - aenu.udio. 0. 0. -H....~ 7 HoauKoiD>1- mi.*dd[0, 0. 0, llnni TextodoT: i-.j
\

sL
>p
retarn. Ber.oBCsi.MOptWMB.nut*): g] } ser.onCEe.teOpeloBsB.nu(nul;
BOvecride 99BOverrlde
PU.UO1.001.onoIto!Beloot,dlU.t lootu-TI, ---- ffl"* 11' ""lo- onJ!ollIt=i^=lootod[U,t ootucold, Honulto1
1
i~ ^ltch (item.getKealStnT itt clMB.getlee-Idl))
{^^ coo0: J||| c0: ...........
Figura 6.16 - Criando os menus com a biblioteca de compatibilidade.
Cap tulo 6 Fragmen ts 235
Pronto!
Se voc seguiu esses passos, seu projeto pode ser executado em um smartphone
com Android 2.x.
Depois que alteramos o projeto para utilizar a biblioteca de compatibilidade
podemos execut-lo em qualquer smartphone Android 2.x, pois no estamos
mais dependendo das novas bibliotecas do Android 3.x.
Para comprovar a teoria de que tudo funciona, a figura 6.17 exibe a aplicao
executando em um smartphone com a tela na horizontal.
30 i| S 10:20
wo^^rold-Cap06-?ramnts 7 :
Fragmen t! T exto 2.
Fragmen t! T exto 1.
Fragmen t! T exto 3.
Figura 6.17 - Exemplos dos fragments em um smartphone utilizando a biblioteca de
compatibilidade.
A figura 6.18 exibe o menu da aplicao aberto. Note que, como a ActionBar
no existe no Android 2.x, o menu fsico tradicional no aparelho foi utilizado.
UvroAndro!d-Cap06*Fragmeht5
:x 11 t 10:20
T exto Atuali zado!
; Fragmen t!T extol.-.-.i rj
; ; ; . 7:. ..
Alterar Texto do Frag 2
Figura 618 - Menu utilizando a biblioteca de compatibilidade.
A biblioteca de compatibilidade permite utilizarmos a API de Fragment, em
verses anteriores ao Android 3.x, de forma a criar um cdigo nico e compatvel
com todas as verses do Android.
236
Google An droi d para T ablets
I mplementar uma aplicao dessa forma ou no vai depender das necessidades
do projeto.
Podemos utilizar a biblioteca de compatibilidade na verso para Android 2.x e
depois reaproveitar os mesmos fragments para a verso 3.x mantendo a biblioteca
de compatibilidade. Ou podemos criar duas activities conforme a API Levei do
aparelho da seguinte forma:
// Verifica se tablet com Android 3.x
boolean android3 = AndroidU tils.isAndroid_ 3();
if (android3) {
// Verso tablets 3.x
startActivity (new Intent(this, M ainTablets.class));
} else {
// Verso 2.x para smartphones
startActivity (new Intent(this, M ainSmartphone.class));
}
Se voc est migrando uma aplicao que j existe no Android 2.x para fun
cionar em tablets 3.x ou superior, essa uma maneira recomendada, pois no
necessrio alterar a verso que j est funcionando e, consequentemente, no
precisamos testar tudo novamente.
Ento podemos criar outra activity, que por sua vez utiliza fragments, mas em
algum lugar teramos um cdigo duplicado em algum ponto entre as duas verses.
Agora, se voc est comeando um novo projeto e sabe desde o princpio que
ele deve funcionar em smartphones e tablets, utilizar a biblioteca de compatibili
dade e preparar os fragments desde o incio pode facilitar bastante a organizao
e reutilizao do cdigo entre as duas verses.
6.14 Detalhes sobre o ciclo de vida de um Fragment
Neste exemplo vamos alterar a classe F ragmenti para demonstrar como salvar o
estado de um fragment utilizando o tradicional mtodo onSavelnstanceState(bundle).
No exemplo a seguir vamos incrementar uma varivel inteira sempre que o
mtodo onResumeO chamado e exibi-la na tela.
public class F ragmenti ex tends DebugF ragment {
private int count = 0;
gO verride
public View onCreateView(LayoutInf!ater inflater, ViehGroup Container, Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragmenti, null);
Cap tulo 6 Fragmen ts 237
Tex tView tex t = (Tex tView) view.findViewByld(R.id.tex tl);
if (savedlnstanceState != null) {
count = savedlnstanceState.getInt(count" );
}
text.setText(''F ragment 1 -> count; " + count);
return view;
}
@ 0verride
public void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
outState.putInt(" count" , count);
}
@ 0verride
public void onResumeQ {
super. onResumeQ ;
count++;
}
}
Para testar este exemplo execute a activity M ainF ragments, que insere os fragments
pelo XML, e fique girando a tela vrias vezes entre a vertical e a horizontal.
Como esse fragment filho de DebugF ragment, ser possvel observar os seguintes
logs no LogCat:
I/livroandroid(3057): F ragment 1 -> onCreate()
I/livroandroid(3057): F ragment 1 -> count: 0
I/livroandroid(3057): F ragment 1 -> onCreateQ
I/livroandroid(3057): F ragment 1 -> count: 1
I/livroandroid(3057): F ragment 1 -> onCreate()
I/livroandroid(3057): F ragment 1 -> count: 2
I/livroandroid(3057): F ragment 1 -> onCreate()
I/livroandroid(3057): F ragment 1 -> count: 3
I/livroandroid(3057): F ragment 1 -> onCreateQ
I/livroandroid(3057): F ragment 1 -> count: 4
I/livroandroid(3057): F ragment 1 -> onCreate()
I/livroandroid(3057): F ragment 1 -> count: 5
I/livroandroid(3057): F ragment 1 -> onCreate()
Conforme o esperado, os logs demonstram que o mtodo onSavelnstanceState(bundle)
salva o estado do fragment normalmente, da mesma forma que ele utilizado
em uma activity.
6.15 O mtodo setRetainlnstance(boolean)
O exemplo anterior demonstrou o ciclo de vida padro de um fragment, que
segue o mesmo princpio da activity, onde a instncia atual destruda para ser
criada uma nova durante as trocas de configurao de sistema.
Outra forma de salvar o estado de um fragment chamar o mtodo
setRetainlnstance(true), o que va-i: fazer com que a instncia do fragment sobreviva
durante a troca de orientao da tela.
Portanto, se chamarmos esse mtodo dentro do fragment, podemos girar a tela
vrias vezes e, ainda assim, a mesma instncia desse fragment ficar em memria
quando a activity for recriada.
Para demonstrar a teoria vamos alterar o cdigo-fonte da classe F ragmenti con
forme demonstrado a seguir.
Note que o mtodo setRetainlnstance(true) chamado no final do mtodo
onCreateView(...) do fragment, e agora no mais necessrio utilizar o mtodo
onSavelnstanceState(bundle).
public class F ragmenti sx tends Debugrragmeni. {
private int count = 0;
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
Log.i(" livroandroid" , "F ragment 1 -> onCreateQ " );
}
gO verride
public View onCreateView(LayoutInflater inflater, Vietroup Container, Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragmenti, null);
Tex tView tex t = (Tex tView) view.findViewBy ld(R.id.textl);
String msg = F ragment 1 -> count: + count;
Log.i("livroandroid'',msg);
tex t.setTex t(msg);
setRetainlnstance(true);
return view;
}
@ 0verride
public void onResumeQ {
super.onResumeQ ;
count+ + ;
}
238 Google An droi d para T ablets
0
Ao executar esse exemplo e girar a tela algumas vezes podemos observar que
o mtodo on CreateQ do F ragment chamado apenas uma nica vez, confirmando
que sua instncia foi preservada.
Note que neste caso podemos salvar o atributo count diretamente na classe, pois
a instncia desse fragment ser mantida durante a troca de orientao.
I/livroandroid(3057): F ragment 1 -> onCreateQ
I/livroandroid(3057): F ragment 1 -> count: 0
I/livroandroid(3057): F ragment 1 -> count: 1
I/livroandroid(3057): F ragment 1 -> count: 2
I/livroandroid(3057): F ragment 1 -> count: 3
I/livroandroid(3057): F ragment 1 -> count: 4
I/livroandroid(3057): F ragment 1 -> count: 5
O mtodo setRetainlnstance(true) utilizado para manter a instncia
do fragment viva durante o ciclo de vida de uma activity, como,
por exemplo, ao trocar a orientao da tela. Esse mtodo pode ser
muito til, dependendo do caso. Para mais detalhes consulte a
documentao oficial.
Cap tulo 6 Fragmen ts ^ 239
6.16 Ciclo de vida de um FragmentTransaction
At o momento foi demonstrado o comportamento do ciclo de vida de um frag
ment quando ele inserido diretamente no layout pelo XML com a tag < frag-
ment> . Mas ao inserir os fragments dinamicamente pela API, o comportamento
diferente, e para relembrar vamos verificar o cdigo-fonte da classe M ainF ragmentsAPI.
( d Mai n Fragmen tsAPI.java
public class M ainF ragmentsAPI ex tends DebugActivity {
@ O verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main_ fragments_ api);
F ragmenti fragl = new F ragmentlQ ;
F ragment2 frag2 = new Fragment2();
F ragment3 frag3 = new F ragment3Q ;
// Adiciona os fragments
F ragmentM anager fm = getF ragmentM anagerQ ;
F ragmentTransaction t = fm.beginTransactionQ ;
t.add(R.id.lay outl, fragl, "fragl" );
t.add(R.id.lay out2, frag2, "frag2" );
t.add(R.id.lay out3, frag3, frag3" );
t.commitO ;
}
}
Neste exemplo, ao girar a tela vrias vezes podemos perceber que sempre
que a activity recriada, uma nova F ragmentTransaction executada para inserir os
fragments, e como podemos observar na figura 6.19, os fragments so duplicados
na tela.
Isso acontece porque o FragmentTransaction persiste durante a troca de orientao,
e quando a activity recriada, a transao volta a executar para recriar o estado
original de antes da troca de orientao.
240 Google An droi d para T ablets
Fragmen t 2 - > coun t: 3
Fragmen t 2 -> coun t: 2
Fragmen t 2 -> coun t: 1
Fragmen t 1 -> c oun t 3 Fragmen t 2 -> coun t: 0
Fragmen t 1 -> coun t: 2
Fragmen t 1 -> coun t: 1
Fragmen t 1 -> coun t: O Fragmen t! T exto 3.
Fragmen t! T exto 3.
! Fragmen t! T exto 3.
Fragmen t! T exto 3.
Figura 6.19 - Fragment duplicados.
Para solucionar esse problema, na inicializao da activity necessrio testar
se o fragment j no est inserido no layout, para somente nesse caso executar
a F ragmentTransaction.
Portanto, vamos alterar o cdigo da classe M ainF ragmentsAPI conforme demons
trado a seguir.
da Mai n Fragmen tsAPI.java
public class M ainF ragmentsAPI ex tends DebugActivity {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedInstanceSt3te);
setContentView(R.lay out,main_ fragments_ api);
F ragmentM anager fm = getSupportF ragmentM anagerQ ;
// Verifica se o F ragment j est adicionado no layout
F ragment fragl = fm.findF ragmentByTag(" fragl" );
Cap tulo 6 Fragmen ts 241
if (fragl == null) {
fragl = new Fragmentl();
F ragment2 frag2 = new Fragment2();
F ragment3 frag3 = new F ragment3();
// Adicinoa os fragments
F ragmentTransaction t = fm.beginTransaction();
t.add(R.id.lay outl, fragl, "fragl");
t.add(R.id.lay out2, frag2, "frag2'');
t.3dd(R.id.lay out3j frag3, "frag3");
t.coraiitO;
}
}
}
Dessa forma, ao executar novamente o exemplo vamos perceber que os frag
ments sero inseridos somente uma vez no layout, conforme o esperado.
Outra forma d implementar seria testar se o Bundle savedlnstanceState est nulo,
o que indica que a primeira vez que a activity criada.
(si Mai n Fragmen tsAPI.java
public class M ainF ragmentsAPI ex tends DebugActivity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main_ fragments_ api);
FragmentM anager fm = getF ragmentM anagerQ ;
// Verifica se a primeira vez q ue a activity est ex ecutando
if (savedlnstanceState == null) {
Fragmentl fragl = new F ragmentl();
Fragment2 frag2 = new F ragment2();
Fragment3 frag3 = new F ragment3();
// Adiciona os fragments
F ragmentTransaction t = fm.beginTransactionQ ;
t.add(R.id.lay outl, fragl, fragl" );
t.add(R.id.lay out2, frag2, " frag2);
t.add(R.id.lay out3, frag3, frag3" );
t.commit();
}
}
}
u Google An droi d para T ablets
Neste captulo estudamos a interessante API de Fragments, que tem o objetivo
de auxiliar os desenvolvedores no desenvolvimento de aplicaes para tablets,
onde diversas views so utilizadas na tela para um bom aproveitamento de espao.
Conforme estudamos, os fragments tambm so componentes reutilizveis
e ajudam a organizar o cdigo, alm de separar as responsabilidades da activity
Tambm estudamos a biblioteca de compatibilidade, que permite utilizar essa
nova API mesmo em smartphones com Android 1.6 ou superior.
No prximo captulo vamos estudar a API da ActionBar, que tambm foi criada
a partir do Android 3.0 Honeycomb.
CAPTULO 7
ActionBar
A partir do Android 3.0 Honeycomb a tradicional barra de status superior foi
substituda por uma nova e turbinada barra de aes, conhecida como a ActionBar.
Nela podemos ter menus, botes com diversas aes, widgets especiais, como o
de busca, separao porTabs e muito mais.
A ActionBar visa conseguir melhor proveito das grandes telas dos tablets e
adicionar recursos extras na barra para tornar a utilizao dos aplicativos mais
simples e intuitiva.
Neste captulo vamos estudar esse interessante recurso, que hoje um dos
padres mais utilizados em aplicaes Android.
7.1 Introduo
A ActionBar um widget especial que substitui a famosa barra de status na parte
superior da tela. Nos tablets ela contm o cone do aplicativo na esquerda, algumas
aes que podem ser separadas por Tabs, e na direita itens de menu que podem
ser adicionados para realizar determinadas aes.
Na figura 7.1 podemos ver a aplicao do Gmail nativa e sua ActionBar custo
mizada na parte superior.
Para exibir a ActionBar devemos ativar o tema holographic, disponvel para o
Android 3.x ou superior. Para isso basta alterar a propriedade android :targetSdk Version
no AndroidManifest.xml para utilizar API Levei = 11ou superior.

A API Levei o cdigo identificador de cada release do Android, e a


11 equivalente ao Android 3.0: android:targetSdk Version= ll"
. ..a, . . - .i i'
243
244
Google An droi d para T ablets
Figura 7.1 -ActionBar do Gmail.
Caso seja necessrio utilizar a API da classe android.app.ActionBar, tambm
devemos compilar o projeto com Android 3.x, porque essa classe no existe em
verses anteriores do Android.
Essa compilao para Android 3.x j foi feita no captulo 5, onde iniciamos a
migrao do aplicativo dos carros para tablets.
7.2 Preparando o projeto de exemplos
Para este captulo vamos continuar o projeto do captulo anterior, onde criamos
vrios fragments na tela. Mas agora vamos estudar algumas funcionalidades
especficas da ActionBar.
Para diferenciar este projeto daquele do captulo anterior basta fazer uma cpia
do projeto LivroAndroid-Cap06-F ragments e trocar o nome para LivroAndroid-Cap07-ActionBar.
7.3 Adicionando itens de menu via API
No captulo anterior vimos como simples inserir um item de menu na Action
Bar utilizando o tradicional mtodo onCreateO ptionsM enu(menu) para criar os menus
da aplicao.
Cap tulo 7 Acti on Bar 245
A seguir podemos verificar o cdigo que usamos anteriormente para inserir
dois itens ha barra de aes.
gO verride
public boolean onCreateO ptionsH enu(H enu menu) {
super.onCreateO ptionsM enu(menu);
H enultem ml = menu.add(0, 0, 0, Remover/Adicionar Frag2");
ml.setShowAsAction(H enuItem.SH O W _ AS_ ACTIO N_ ALW AY S);
H enultem m2 = menu.addto, 1, 1, " Trocar o F rag 3 pelo 4");
m2.set5howAsAction(H enuItem.SH O W _ AS_ ACTIO N_ ALW AY S);
return true;
}-
A partir do Android 3.x foi criado o mtodo setShowAsAction(tipo) na classe Menultem,
para facilitar a criao destes itens de menu e utilizando uma API j conhecida
pelos desenvolvedores.
O mtodo setShowAsAction(tipo) na classe H enultem foi criado a partir
do Android 3.x, portanto, para utilizar esse mtodo necessrio
compilar o projeto com Android 3.x ou superior.
A seguir temos a explicao dos parmetros que podem ser passados para o
mtodo setShowAsAction{ tipo) para informar a forma que a ao ser disponibilizada
na ActionBar. Por exemplo, podemos exibir uma ao com um texto e com um cone
opcional, ou deix-la no menu de contexto, que pode ser aberto com todas as opes.
Parmetro Descrio
SH O W _ AS_ ACTIO N_ ALW AY S
Sempre exibe este item como um boto. recomendado utilizar
esta configurao com precauo, pois se muitos itens de menu
tiverem esta opo habilitada, pode faltar espao na ActionBar
para inserir todos os itens.
SH 0W _ AS_ ACTI0N_ IF _ RO O H
Exibe este item como um boto, se existir espao disponvel.
Esta configurao geralmente a recomendada, pois dessa
forma os itens somente so inseridos na ActionBar se existir
espao suficiente, permitindo que o Android controle a
disposio dos elementos.
SH O W _ AS_ ACTIO N_ NEVER
Nunca exibe este item como um boto. Podemos utilizar
esta configurao para aqueles itens que desejamos que
obrigatoriamente fiquem no menu com mais opes, que ser
aberto quando o usurio selecionar este item no canto superior
direito para visualizar todos os itens de menu.
SH O W _ AS_ ACTIO N_ W ITH _ TEX T
Sempre exibe este item como um boto, mas utilizando um
texto. Esta configurao pode fazer o item de menu ocupar mais
espao, porque ao lado do cone tambm exibido um texto.
No prximo exemplo vamos ver na prtica o funcionamento dessas constantes.
246
Google An droi d para T ablets
7.4 Adicionando itens de menu via XML
No A ndroid muitas das configuraes podem ser feitas utilizando arquivos XML,
e para criar menus no diferente.
Para alterar a criao dos itens de menu na ActionBar vamos criar um arquivo
/res/menu/menu.xml conforme demonstrado a seguir.
) /res /men u /me n u.xml
< ? x ml version= " 1.0" encoding= " utf-8?>
< menu x mlns:android= " http ://schemas.android.com/apk /res/android" >
< item android:id= " @ + id/menul"
android: icon= " @ drawable/smilel"
android:title= " Iten 1
android:showAsAction= " ifRoom" />
< item android:id= ''g+id/menu2"
android:icon= " gdrawable/smile2
android:title= Iteni 2"
android:showAsAction= " ifRoom| withTex t" />
< item android:id= " @ + id/menu3"
android:title= " Item 3"
android:showAsAction= " never" />
No XML podemos utilizara propriedade android :showAsAction para informar os pa
rmetros para insero desse item de menu. Por exemplo, android: showAsAction= " ifRoom"
eqivale a utilizar a constante s h o w _ a s _ a c t i o n _ i f _ r o o m pela API.
Note que tambm estamos utilizando imagens para os itens de menu, com
o atributo android:icon= " gdrawable/smilel" J para melhorar o visual da aplicao. As
imagens podem ser encontradas no link de download do projeto.
Feito isso, podemos alterar o mtodo que cria o menu para simplesmente inflar
o XML conforme demonstrado a seguir.
gO verride
public boolean onCreateO ptionsM enu(M enu menu) {
super.onCreateO ptionsM enu(menu);
Menulnflater inflater = getHenuInflater();
inflater. inflate(R.menu .menu, menu);
return true;
}
</menu>
<33
052
iEC
ST
Os arquivos XML de menu devem ser inseridos na pasta Ires/menu.
Captulo 7* ActionBar 247
&
Para validar os conceitos vamos criar a activity M ainActionBar da seguinte forma.
di Mai n Acti on Bar.java
public class M ainActionBar ex tends DebugActivity {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out ,main_ fragments_ api);
F ragmentl fragl = new Fragmentl();
F ragment2 frag2 = new Fragment2();
F ragmentB frag3 = new Fragment3();
// Adiciona os fragments
F ragmentH anager fm = getF ragmentM anagerQ ;
F ragmentTransaction t = fm.beginTransactionQ ;
t.add(R.id,lay outl, fragl, "fragl");
t.add(R.id.lay out2, frag2, "frag2");
t .add(R.id.lay out3, frag3, "frag3");
t.commitQ ;
}
(SOverride
public boolean onCreateO ptionsM enu(M enu menu) {
super.onCreateO ptionsM enu(menu);
Menulnflater inflater = getHenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
@ 0verride
public boolean onM enuItemSelected(int featureld, M enultem item) {
// Clicou no menu
switch (item.getltemldQ ) {
case R.id.menul:
Toast.mak eTex t(this, "Item 1", Toast.LENGTH _ SH O RT).show();
break;
case R.id.menu2:
Toa5t.mak eTex t(this, Item 2", Toast.LENGTH _ SH O RT).show();
break;
case R.id.menu3:
Toast.mak eTex t(this, "Item 3", Toast.LENGTH _ SH O RT).show();
break;
}
return true;
}
}
243
Google An droi d para T ablets
Antes de executar este exemplo lembre-se de adicionar a activity no Android-
Manifest.xml, que pode ser visualizado a seguir.
( d An droi dMan i f est.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.com/apk /res/android"
pack age= " br.livroandroid.cap06.fragments"
android:versionCode= " l"
android:versionName= " 1.0" >
< uses-sdk android:minSdk Version= ' ' H " />
opplication android:icon= " @ drawable/icon" android:label= @ string/app_ name
android:theme= " gandroid:sty le/Theme.H olo. Light" >
< activity android:label= " @ string/app_ name" android:name= " .M ainM enu" >
<intent-filter >
< action android:name= " android.intent.action.M AIN" />
< category android:name= " android.intent.category .LAU NCH ER" />
</intent-filter>
< /activity >
< activity android:name= " .H ain" />
< activity android:name= " .M ainF ragments" />
ctivity android:name= " .M ainF ragmentsAPI" />
< activity android:name= " .M ainActionBar" />
< /application>
< /manifest>
Ao executar este exemplo podemos visualizar os fragments na tela, assim como
os itens de menu que esto na ActionBar, conforme a figura 7.2.
Note que o primeiro item est configurado com android:showAsAction= " ifRoom" e
somente exibe a imagem. J o segundo item est configurado com android :showA
sAction= ' ' ifRoom| withTex t" e exibe a imagem ao lado do labei, e o terceiro item fica
escondido, pois est configurado como android:showAsAction= " nevere somente fica
visvel ao expandir o menu com mais opes.
7.5 Utilizando o cone do aplicativo como a home
Um padro muito comum ao utilizar a ActionBar utilizar o boto home, que fica
localizado no canto superior esquerdo, como um atalho para a aplicao voltar
tela de entrada ou ao menu principal.
A figura 73 exibe a tela principal da aplicao que vamos desenvolver execu
tando em um tablet.
Cap tulo 7 Acti on Bar
249
^ $ Item2 =s
Figura 72 - Exemplo com fragments e ActionBar.
Esportivos
Livro
GoogleAndroid
EsW livro dedicado aos desenvolvedores Android que desejam aprimorar
sei conhecimentos e estudar as novas funcionalidades disponveis no
Android 3.x. como fragments e actionbar.
S:--
l j v ..V V. -t ' ' .- - '!> ' : . J,-. .!?. ; . V i M ' , . " ' V
figura 7.3 - Tela inicial no estilo dashboard do aplicativo dos carros em um tablet.
Ao selecionar algum tipo de carro no dashboard, como, por exemplo, os espor
tivos, a tela principal da aplicao com a lista de carros exibida na esquerda, e
os detalhes na direita, conforme a figura 7.4.
i
i
f
C
E

-
C
A
f
V
i
P
i
j
S

F
O
R
T
A
L
E
Z
A

I

1
B
I
B
L
I
O
T
E
C
A

250
Google An droi d para T ablets
Note que no canto superior esquerdo podemos visualizar a imagem do cone
da aplicao com uma seta apontando para a esquerda, indicando que temos um
link nesse item, que, se pressionado, vai voltar tela principal.
Carros 3.. viAVLuxo '. Esportivos -^Clssicos.
AUOtOTSpyder
CXKn(Jo
Ponche Pi namera
l amb#r| hlnl Aventadoc
Def tj ul t Meganc RS
Trophy
DnMfl
Maierali Cnnubri o
Sport
Dncr>(lo
Uf RCEDCS-IENZ c u
AMQ
Outro modelo que estreou em Genebra, conta comum desempenho estim*do para f uer 0-100 km/ h em 2.9 segundo* e uma veloadade
mi i mj de350 km/h. Eequipado com t motor 6.5 que gera 700 cavalos de pot inda am o uso intevoem fibra de carbono. Ele o
modelo mas rapido epotente que a lamborghini prodi uiu.. Sua preduio ser limitada a4000 unidades (4099 Murolagos foram
produ/ idasv A lamborghmi afirma que o Avemador est l duas geraOes na frente de qualquer carro avenda, usando suspertsto irapuida
em carros da Frmula 1e chassfeitos em fibra de carbono.
Figura 74 - ActionBar sendo utilizada do aplicativo dos carros.
Esse link chamado de menu home, neste caso, ter o mesmo funcionamento do
boto voltar, uma vez que nosso projeto simples e basicamente temos apenas
duas telas.
Mas em outros casos, se tivermos vrias outras telas, o objetivo desse link ser
sempre voltar para a tela inicial, geralmente o menu da aplicao, e esse um
padro extremamente difundido no mundo Android.
Para habilitar esse cone basta adicionar a seguinte linha de cdigo:
ActionBar actionBar = getActionBar();
actionBar.setDisplay H omeAsU pEnabled(true);
Depois disso podemos monitorar se o usurio pressionou o boto home na parte
superior, da mesma forma como qualquer outro item do menu. Para identificador
o menu home podemos utilizar a constante android.R.id.home da seguinte forma:
@ 0verride
public boolean onM enuItemSelected(int featureld, H enultem item) {
// Clicou no menu
switch (item.getltemldQ ) {
Cap tulo 7 Acti on Bar
251
case android.R.id.home:
Toast.mak eTex t(this, " Voltar para a pgina inicial" ,Toast.LENGTH _ SH O RT) ,show();
break;
}
}
Para testar o funcionamento do item de menu home vamos alterar o exemplo
anterior para ativar essa funcionalidade.
S Mai n Acti on Bar.java
public class M ainActionBar ex tends DebugActivity {
gO verride
i public void onCreate(Bundle savedlnstanceState) {
! super.onCreate(savedlnstanceState);
I setContentView(R.lay out.main_ fragments_ api);
Fragmentl fragl = new F ragmentlQ ;
Fragment2 frag2 = new F ragment2();
FragmentB frag3 = new F ragment3();
I // Adiciona os fragments
! F ragmentM anager fm = getF ragmentM anager();
FragmentTransaction t = fm.beginTransaction();
t .add(R.id.layoutlj fragl, "fragl");
i t.add(R.id.lay out2, frag2, "frag2");
j t.add(R.id.lay out3, frag3, "frag3");
i t.commit();
// Ativa o cone " H ome
l ActionBar actionBar = getActionBar();
actionBar.setDisplay H omeAsU pEnabled(true);
}
gO verride
public boolean onCreateO ptionsM enu(M enu menu) {
super.onCreateO ptionsM enu(menu);
Henulnflater inflater = getHenuInflater();
inflater. inflate(R.menu.menu, menu);
return true;
}
gO verride
public boolean onH enuItemSelected(int featureld, M enultem item) {
// Clicou no menu
switch (item.getltemldO ) {
case android.R.id.home:
Toast.mak eTex t(this, " Voltar para a pgina inicial" ,Toast.LENGTH _ SH O RT).show();
break;
252
Google An droi d para T ablets
case R.id.menul:
Toast.mak eTex t(this, " Item 1", Toast.LENGTH _ SH O RT).show();
break;
case R.id.menu2:
Toast.mak eTex t(this, " Item 2, Toast.LENGTH _ SH O RT).show();
break;
case R.id.menu3:
Toast.mak eTex t(this, " Item 3", Toast.LENGTH _ SH O RT).show();
break ;
}
return true;
}
}
Ao executar este exemplo podemos visualizar que o cone da aplicao na
ActionBar possui um link para a esquerda, indicando que o item home possui uma
ao. Mas neste caso estamos apenas exibindo um simples alerta ao clicar nesse
item, conforme a figura 7.5.
Figura 7.5 - ActionBar com o item home.
Na aplicao dos carros que estamos construindo, a qual vamos customizar
para tablets no prximo captulo, vamos utilizar esse recurso para voltar para a
tela principal, onde est o dashboard.
Ao fazer isso devemos limpar a activity stack (pilha de atividades) para destruir
as outras telas, uma vez que no sero mais necessrias.
Para implementar essa funcionalidade podemos utilizar o flag Intent.F LAG_ ACTIVI-
TY _ CLEAR_ TO P ao criar a intent para iniciar uma nova activity Esse flag vai desempilhar
toda a pilha com as telas, deixando no topo somente a activity na qual estamos
interessados, que neste caso a tela inicial.
O cdigo a seguir demonstra como abrir uma activity utilizando esse flag. Ve
remos mais detalhes na prtica posteriormente, ao continuar o projeto dos carros.
Intent intent = new Intent(this, Activity PrincipalAq ui.class);
intent.addF lags(Intent.F LAG_ ACTIVITY _ CLEAR_ TO P);
startActivity (intent);
7.6 Trabalhando com Tabs
Um recurso muito interessante que vamos utilizar no projeto dos carros e que
frequentemente utilizado em aplicaes para tablets Android a navegao por
Tabs com o novo componente disponvel na ActionBar.
Habilitar a navegao por Tabs bem simples, basta uma linha de cdigo.
ActionBar actionBar = getActionBarQ ;
actionBar.setNavigationM ode(ActionBar.NAVIGATIO N_ M O DE_ TABS);
Feito isso, basta chamar o mtodo addTab(tab) da classe ActionBar, para inserir
uma Tab conforme o demonstrado a seguir.
Tab tab = actionBar.newTab();
tab.setText(''TAB Teste");
tab.setIcon(R.drawable.srailel);
tab.setTabListener(implementar ActionBar.TabListener aqui);
actionBar.addTab(tab);
Depois de criar as Tabs podemos implementar a interface android.app.ActionBar.
TabListener para monitorar os eventos gerados, para que seja possvel executar
alguma ao sempre que uma Tab for pressionada, da seguinte forma:
private class NavegacaoTabListener implements ActionBar.TabListener {
public NavegacaoTabListener() {
}
public void onTabSelected(Tab tab, F ragmentTransaction ft) {
// A tab foi selecionada
Cap tulo 7 Acti on Bar 253
}
254 Google An droi d para T ablets
&
public void onTabU nselected(Tab tab, F ragmentTransaction ft) {
// A tab foi desselecinoada
}
public void onTabReselected(Tab tab, F ragmentTransaction ft) {
// A tab foi selecionada novamente
}
}
Esse listener ser chamado quando o usurio escolher uma Tab, e no projeto
dos carros vamos utilizar esse recurso para fazer a troca entre os tipos clssicos,
de luxo e esportivos.
Para demonstrar como alterar o contedo da tela vamos novamente apresentar
um exemplo que utiliza a classe F ragmentTransaction.
Para isso vamos criar uma classe chamada NavegacaoTabListener, que recebe um
fragment no construtor, para que possamos substituir o fragment atual que est
na tela por esse informado no construtor dessa classe.
O cdigo-fonte dessa classe pode ser visualizado a seguir, a qual ser criada
como uma classe interna dentro de outra activity que vamos criar logo na seqncia.
private class NavegacaoTabListener implements ActionBar.TabListener {
private F ragment frag;
public NavegacaoTabListener(F ragment frag) {
// Ao criar o listener para a Tab, indicamos qual o fragment ao q ual ela pertence
this.frag = frag;
}
public void onTabSelected(Tab tab, F ragmentTransaction ft) {
// Adiciona o F ragment desta Tab
ft.replace(R.id.lay outl, frag, null);
}
public void onTabU nselected(Tab tab, F ragmentTransaction ft) {
// Q uando uma Tab perde o foco, ns a removemos
ft.remove(frag);
}
public void onTabReselected(Tab tab, F ragmentTransaction ft) {
}
}
Para testar as Tabs vamos criar uma activity chamada M ainActionBarTabs.
Neste xemplo vamos criar duas Tabs, a primeira'para adicionar no layout o
F ragment4, e a segunda para adicionar o F ragments. Para isso uma F ragmentTransaction
ser utilizada para substituir o fragment atual, que est sendo exibido pelo novo
fragment correspondente Tab selecionada.
Cap tulo 7 Acti on Bar
f e Mai n Acti on BarT abs.java
public class M ainActionBarTabs ex tends DebugActivity {
(JOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main_ fragments_ api);
F ragmenti fragl = new F ragmentlQ ;
F ragment2 frag2 = new F ragment2();
F ragment3 frag3 = new Fragment3();
F ragment4 frag4 = new Fragment4();
F ragments frag5 = new F ragment5();
// Adiciona os fragments
F ragmentM anager fm = getF ragmentM anagerQ ;
F ragmentTransaction t = fm.beginTransaction();
t.add(R.id.lay outl, fragl, "fragl" );
t.add(R.id.lay out2, frag2, "frag2" );
t.add(R.id.lay out3, frag3, frag3" );
t.commit();
// Ativa a navegao por Tabs
ActionBar actionBar = getActionBar();
actionBar.setNavigationM ode(ActionBar.NAVIGATIO N_ M O DE_ TABS);
// Cria as Tabs
Tab tab = actionBar.newTab();
tab.setTex t(" F rag
tab.setlcon(R.drawable.smilel);
tab.setTabListener(new NavegacaoTabListener(frag4));
actionBar.addTab(tab);
Tab tab2 = actionBar.newTab();
tab2.setIcon(R.drawable.smile2);
tab2.setTex t(" F rag 5");
tab2.setTabListener(new NavegacaoTabListener(fragS));
actionBar.addTab(tab2);
}
@ 0verride
public boolean onCreateO ptionsM enu(H enu menu) {
super. onCreateO ptionsH enu(menu);
Henulnflater inflater = getMenuInflater();
inflater. inflate (R. menu. menu, menu);
return true;
}
@ 0verride
public boolean onM enuItemSelected(int featureld, H enultem item) {
// Clicou no menu
256
Google An droi d para T ablets
switch (item.getltemldQ ) {
case android.R.id.home:
Toast.mak eTex t(this, " Voltar para a pgina inicial" ,Toast.LENGTH _ SH O RT).show();
break;
case R.id.menul:
Toast.mak eTex t(this, " Item 1", Toast.LENGTH _ SH O RT).show();
break;
case R.id.menu2:
Toast.mak eTex t(this, " Item 2", Toast.LENGTH _ SH O RT).show();
break;
case R.id.menu3:
Toast.mak eTex t(this, " Item 3", Toast.LENGTH _ SH O RT).show();
break ;
}
return true;
} -
// Listener para q uando clicar na Tab, trocar o fragment
private class NavegacaoTabListener implements ActionBar.TabListener {
private F ragment frag;
public NavegacaoTabListener(F ragment frag) {
// Ao criar o listener para a Tab, indicamos q ual o fragment ao q ual ela pertence
this.frag = frag;
}
public void onTabSelected(Tab tab, F ragmentTransaction ft) {
// Adiciona o F ragment desta Tab
ft.replace(R.id.lay outl, frag, null);
}
public void onTabllnselected(Tab tab, F ragmentTransaction ft) {
// Q uando uma Tab perde o foco, ns a removemos
ft.remove(frag);
}
public void onTabReselected(Tab tab, F ragmentTransaction ft) {
}
}
}
Para alterar o fragment dinamicamente pela F ragmentTransaction vamos utilizar
o identificador R.id.lay outl que foi criado no arquivo / r e s / layout/main.xml. Apenas
para relembrar, segue o trecho de cdigo que cria esse layout da esquerda.
< ! -- Bloco 1 -->
< LinearLay out
android:id= " @ + id/lay outl"
android:orientation= " vertical"
Cap tulo 7 Acti on Bar
257
android:lay out_ width= " wrap_ content"
android: layout_height= "fill_ parent"
android:back ground= " # eeeeee"
android:lay out_ weight= " l"
android:lay out_ margin= " 10dp"
android:gravity = " center"
>
< Tex tView
android:id="(8+id/textl"
android:lay out_ width= " wrap_ content"
android: lay out_ height= " wrap_ content"
android:tex t= " Tex to 1"
android:tex tSiz e= " 18sp"
android:tex tSty le= " bold"
android:tex tColor= " # 000000"
/>
</LinearLay out>
A figura 7.6 exibe o resultado desse exemplo. Conforme o esperado, ao selecio
nar a Tab com o texto Frag 4 o fragment F ragment4 ser adicionado na esquerda
do layout.
Figura 76 - Exemplo com ActionBar e Tabs.
258 Google An droi d para T ablets
7.7 I nserindo uma view customizada na barra
Quando criamos um item de menu na ActionBar podemos fornecer tambm uma
view opcional para esse item.
Para isso podemos utilizar o mtodo M enultem. setActionView(view) da seguinte forma:
M enultem item = ?;
View view = ?
item.setActionView(view);
Uma das views mais utilizadas na ActionBar para um item de menu o SearchView,
que inicialmente exibe apenas um cone com uma lupa que, quando pressionado,
se autoexpande para que algum texto seja digitado para que se efetue a busca.
A figura 7.7 demonstra o resultado do prximo exemplo que vamos criar uti
lizando o componente SearchView.
UvroAndroid-CapO-Fragments (| g)frag 4 @ F r ag 5 Q,, Digite algo aqui
Fragmentl Texto 4.
Fragment! Texto 2.
Fragment! Texto 3.
Figura 7.7 - Exemplo com ActionBar e SearchView.
Para este novo exemplo primeiramente vamos criar uma nova configurao
de menu. Ao utilizar o SearchView recomenda-se deix-lo como o primeiro item
do menu e tambm no encher a ActionBar com outros itens, para no extrapolar
o espao disponvel.
Para isso geral mente recomendado dei xar os i tens com a opo
android:showAsAction= ,,ifRoom" ou, melhor ainda, android:showAsAction= " never" .
I
l
Cap tulo 7 Acti on Bar
o
i a /res/men u/men u2_search_vi ew.xml
259
<?x ml version= " 1.0" encoding= utf-8,'?>
< menu x mlns :android=''http: //schemas.android.com/apk /res/android" >
< item
android:id=" @ + id/menu_ busca"
android:icon= " gandroid:drawable/ic_ menu_ search"
android:title= " Busca"
android:showAsAction= " ifRoom[withTex t"
/>
< item
android:id=" @ + id/menu2"
android:icon= " @ drawable/smile2"
android:title= " Item 2"
android: showAsAction="ifRoom'' />
< item
android:id="@ + id/menu3"
android:title= " Item 3"
android:showAsAction= " never" />
</men u>
Esse menu igual aos exemplos anteriores e possui trs itens. O primeiro item
ser substitudo pelo SearchView, e at o momento no existe nada de especial nele.
Um detalhe importante sobre esse item de menu que utilizamos uma ima
gem nativa com a sintaxe android:icon= " @ android:drawable/icj nenu_ search' ' para utilizar
a imagem da lupa.
O prximo passo criar o SearchView dinamicamente ao inflar o menu e utilizar
o mtodo M enultem.setActionView(view) para customizar a view desse item de menu.
gO verride
public boolean onCreateO ptionsM enu(H enu menu) {
super.onCreateO ptionsM enu(menu);
Menulnflater inflater = getMenuInflaterQ ;
inflater.inflate(R.menu.menu2_search_view, menu);
M enultem item = menu.findItem(R.id.menu_ busca);
SearchView sv = new SearchView(this);
sv.setO nQ uery ex tListener(new M enuListener(this));
item.setActionView(sv);
return true;
}
Note que depois de inflar o menu o item de busca recuperado pelo id, para
que seja possvel criar um SearchView e configur-lo como a view desse item.
260
Google An droi d para T ablets
O Searchview inicialmente exibe apenas uma lupa com o cone da busca, e ao
ser pressionado ir exibir o campo para que se digite algum texto.
Para receber a informao digitada podemos criar um listener que implementa
a interface android.widget.SearchView.O nQ uery Tex tListener. Portanto, vamos utilizar a
classe M enuListener conforme demonstrado a seguir.
static class M enuListener iraplements O nQ uery Tex tListener {
private Contex t context;
public M enuListener(Contex t context) {
this.contex t = context;
}
@ 0verride
public boolean onQ uery Tex tChange(String tex toParcial) {
Log.i(" livroandroid' ," onQ uery Tex tChange: " + tex toParcial);
return false;
}
(W verride
public boolean onQ uery Tex tSubmit(String tex toF inal) {
Log.i(livroandroid/onQ uery Tex t5ubniit: " + tex toFinal);
Toast.mak eTex t(contex t, "Texto: " + tex toF inal, Toast.LENGTH _ L0NG).show();
return false;
}
}
E isso tudo.
Depois de adi cionar o S e a r c h v i e w basta i mpl ementarmos os mtodos
onQ uery Tex tChange(tex to) e onQ uery Tex tSubmit(tex to) para recebermos informaes de
quando o texto mudou e de quando a busca final foi disparada pelo usurio.
Neste exemplo estamos apenas exibindo um alerta na tela, mas posteriormente,
em nosso aplicativo dos carros, vamos utilizar essa busca para filtrar a lista dos
carros que so exibidos.
A seguir podemos visualizar o cdigo-fonte completo deste exemplo.
& Mai n Acti on BarT absSearchVi ew.java
public class M ainActionBarTabsSearchView ex tends DebugActivity {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main_fragraents_ api);
F ragmenti fragl = new F ragmentlQ ;
F ragment2 frag2 = new Fragment2();
Cap tulo 7 Acti on Bar
F ragment3 frag3 = new F ragment3();
F ragment4 frag4 = new F ragment4();
F ragment5 frag5 = new F ragment5();
// Adiciona os fragments
F ragmentM anager fm = getF ragmentH anager();
F ragmentTransaction t = fm.beginTransaction();
t.add(R.id.lay outlj fragl, "fragl");
t.add(R.id.lay out2, frag2, " frag2);
t.add(R.id.lay out3, frag3, "frag3" );
t.commitQ ;
// Ativa a navegao por Tabs
ActionBar actionBar = getActionBar();
actionBar.setNavigationH ode(ActionBar.NAVIGATIO N_ M O DE_ TABS);
// Cria as Tabs
Tab tab = actionBar.newTabQ ;
tab.setTex t(Frag 4");
tab.setIcon(R.drawable.smilel);
tab.setTabListener(new NavegacaoTabListener(frag4));
actionBar.addTab(tab);
Tab tab2 = actionBar.newTab();
tab2. setIcon(R.drawable.smile2);
tab2.setTex t(" F rag 5");
tab2.setTabListener(new NavegacaoTabListener(frag5));
actionBar.addTab(tab2);
}
gO verride
public boolean onCreateO ptionsM enu(M enu menu) {
super.onCreateO ptionsH enu(menu);
Henulnflater inflater = getHenuInflater();
inflater.inflate(R.menu.menu2_ search_ viewJ menu);
H enultem item = menu.findItem(R.id.menu_busca);
Searchview sv = new SearchView(this);
sv.setO nQ uery Tex tListener(new H enuListener(this));
item.setActionView(sv);
return true;
}
gO verride
public boolean onM enuItemSelected(int featureld, H enultem item) {
// Clicou no menu
switch (item.getltemld()) {
case android.R.id.home:
Google An droi d para T ablets
Toast.mak eTex t(this, " Voltar para a pgina inicial" ,Toast.LENGTH _ SH O RT).show();
return true;
case R.id.menul:
Toast.mak eTex t(thi5, " Item 1", Toast.LENGTH _ SH O RT).show();
return true;
case R.id.menu2:
Toast.mak eTex t(this, " Item 2", Toast.LENGTH _ 5H 0RT).show();
return true;
case R.id.menu3:
Toast.mak eTex t(this, Item 3", Toast.LENGTH _ SH O RT).show();
return true;
}
return super.onM enuItemSelected(featureId, item);
}
// Listener para q uando clicar na Tab, trocar o fragment
private class NavegacaoTabListener implements ActionBar.TabListener {
private F ragment frag;
public NavegacaoTabListener(F ragment frag) {
// Ao criar o listener para a Tab, indicamos q ual o fragment ao q ual ela pertence
this.frag = frag;
}
public void onTab5elected(Tab tab, F ragmentTransaction ft) {
// Adiciona o F ragment desta Tab
ft.replace(R.id.lay outl, frag, null);
}
public void onTabllnselected(Tab tab, F ragmentTransaction ft) {
// Q uando uma Tab perde o foco, ns a removemos
ft.remove(frag);
}
public void onTabReselected(Tab tab, F ragmentTransaction ft) {
}
}
// Listener para as buscas do menu
static class M enuListener implements O nQ uery Tex tListener {
private Contex t context;
public M enuListener(Contex t contex t) {
this.contex t = context;
}
gO verride
public boolean onQ uery Tex tChange(String tex toParcial) {
Log.i(" livroandroid" ," onQ uery Tex tChange: + tex toParcial);
return false;
Cap tulo 7 Acti on Bar
263
@ 0verride
public boolean onQ uery Tex tSubmit(String tex toF inal) {
Log.i(" livroandroidY ' onQ uery Tex tSubmit: " + tex toF inal);
Toast.mak eTex t(contex t, "Texto: " + tex toF inal, Toast.LENGTH _ LO NG).show();
return false;
}
}
}
7.8 Mtodos utilitrios da ActionBar
A classe android.app.ActionBar possui vrios mtodos que permitem customizar o
ttulo, o cone home e inclusive o seu tema.
Um dos mtodos mais utilizados o setBack groundDrawable(drawable) para informar
uma imagem de fundo para a ActionBar.
No aplicativo dos carros que vamos continuar a desenvolver no prximo
captulo vamos customizar o fundo da ActionBar com a imagem /res/dmivable/sha-
pejieader.xml que criamos anteriormente com o gradiente azul da seguinte forma:
// Altera o plano de fundo da ActionBar
ActionBar actionBar = getActionBar();
actionBar. setBack groundDrawable(getResources().getDrawable(R.drawable.shape_ header));
Outro mtodo interessante que temos o setDisplay H onieAsU pEnabled(true), que
ativa o cone home, que fica localizado no canto superior esquerdo e utilizado
pela aplicao para navegar at a tela inicial.
A utilizao desse mtodo simples e no requer prtica nem habilidade.
actionBar. setDisplay HomeAsl)pEnabled(true);
Quando formos customizar a nossa aplicao para tablets vamos criar uma
tela com a listagem dos carros e com a navegao por Tabs na ActionBar, conforme
a figura 7.8. O cone homeser ativado para que, se pressionado, a aplicao volte
para a tela inicial com o dashboard.
// Liga a navegao por Tabs
ActionBar actionBar = getActionBarQ ;
actionBar.setNavigationM ode(ActionBar.NAVIGATIO N_ M O DE_ TABS);
actionBar. setBack groundDrawable(getResources().getDrawable(R.drawable.shape_ header));
actionBar.setDisplay H omeAsU pEnabled(true);
264 Google An droi d para T ablets
Figura 7.8 - Exemplo com ActionBar e SearchView.
Outros mtodos que podem ser teis so o show() e o hide(), que so utilizados
para exibir ou esconder a ActionBar. Por exemplo, para esconder a ActionBar
poderamos utilizar um cdigo como este:
ActionBar actionBar = getActionBar();
actionBar.hide()j
Existem outros mtodos utilitrios na ActionBar, mas esses so os mais utilizados.
Consulte a documentao para obter a lista completa.
7.9 ActionBar em verses anteriores ao Android 3.x
Uma pergunta comum dos desenvolvedores como utilizar os Fragments ou
ActionBar em verses anteriores ao Android 3.x.
Para os fragments existe a possibilidade de utilizar a biblioteca de compatibi
lidade e se beneficiar da classe android.support.v4.app.F ragment, conforme vimos no
captulo anterior. Mas para a ActionBar no existe ainda nenhum suporte nativo.
Um dos projetos que demonstram como implementar a ActionBar tanto em
smartphones 2.x quanto nos tablets 3.x o ioshed do Google, apresentado no
Google I /O 2011, e seu cdigo-fonte pode ser visualizado neste endereo:
http-J/code.google.com/p/ioschedJ
A figura 7.9 exibe a aplicao ioshed executando.
Cap tulo 7 Acti on Bar 265
Figura 7.9 - ActionBar na aplicao ioshed.
Basicamente, para implementar uma ActionBar no Android 2.x necessrio
que se crie uma view manual, da mesma forma que o include_header.xml de nosso
exemplo.
Uma verso simplificada do ioshed que demonstra como criar a ActionBar
em verses anteriores ao Android 3.x pode ser encontrada no SDK do Android
4.x Ice Cream Sandwich com o nome de ActionBarCompat.
Afigura 7.10 exibe o projeto ActionBarCompat executando em um tablet com
Android 3.x e em um smartphone com Android 2.x.
Existem outros projetos open-surce que tambm ajudam o desenvolvedor
a criar uma ActionBar em verses anteriores do Android, e no difcil encontrar
exemplos, basta uma boa pesquisa no Google. A mais famosa delas a Action-
BarSherlock e por ser bem simples de configurar nem vem ao caso explicar aqui.
Basicamente consiste em adicionar esta biblioteca no projeto e substituir as classes
nativas pelas da biblioteca.
266
Google An droi d para T ablets
.jtjSt no*.
Figura 710 - Exemplo de ActionBar disponvel no SDK do Android.
No prximo captulo vamos iniciar a migrao de nosso projeto para tablets
com A ndroid 3.x e vamos utilizar os novos recursos de Fragments e ActionBar
que estudamos nesses ldmos captulos.
No caso da ActionBar, vamos utiliz-la somente na aplicao para tablets e
smartphones com Android 4.x ou superior, e para os smartphones com Android
1.6 e 2.x vamos deixar o visual como est. Isso facilitar a criao da verso para
tablets sem interferir na verso para smartphones que j est funcionando.
At o prximo captulo!
)
.W.WWU L J U J J WJ M M J U
(
CAPTULO 8
Migrando o aplicativo
para tablets
At o momento criamos uma aplicao que exibe a lista de carros para smar
tphones com Android 2.x e customizamos a tela para funcionar com um tablet
de 7 como o Samsung Galaxy Tab.
Neste captulo, para aproveitar ao mximo os novos recursos disponveis nos
tablets com Android 3.x, vamos criar uma verso especfica do aplicativo dos
carros para tablets utilizando as novas APIs Fragments e ActionBar.
Para duplicar poucos cdigos entre as verses smartphone e tablet vamos
utilizar a biblioteca de compatibilidade para criar os fragments e refatorar aos
poucos a verso para smartphones.
No final do captulo teremos uma aplicao que, com uma nica verso, ir
executar em smartphones e tablets.
8.1 Introduo
Chegou o momento de migrarmos nossa aplicao para o Android 3.x e utilizar
as novas APIs Fragments e ActionBar, que estudamos nos captulos anteriores.
O nosso objetivo customizar nossa aplicao para as telas grandes de um
tablet com Android 3.x, mas ainda permitir que a aplicao seja instalada em
smartphones com Android 2.x. Dependendo da caracterstica do aparelho, a
aplicao vai exibir a verso para smartphones ou tablets.
267
268 Google An droi d para T ablets
Entenda que quando' informamos que o aplicativo vai executar em
8. tablets com Android 3.x, queremos dizer que pode ser Android 3.x ou...
l. superior; desde que seja um tablet. Para smstphones vaTeajnesma
; coisa. Podemos ter smartphones com Android 2.x,'ms?afm^ma
'" verso vai executar sm problemas no Android 4.x ICS. Basicamente,
l . nosso1aplicativo ter duas verses, uma pa smartphries e outra
para tablets, caso . estes possuam o Android 3.x Honeycomb ou
^ superior. ;'.!> ___ :i P
A figura 8.1 exibe o nosso projeto atual executando em um smartphone.
4
i l
Clssicos Luxo
Livro
Google
Android
Google
AnDROI D
Esportivos Sobre
Este livro dedicado aos
desenvolvedores Android que
desejam aprimorar seus
conhecimentos e estudar as novas
funcionalidades disponveis no
Android 3.x, como fragments e
actionbar.
Porsche Panamera
Lamborghini Aventador
Chevrolet Corvette Z06
BMW M5
Renault Megane RS Trophv >
Smartphones e Celulares f y - 1-'*
IG. Se rr? Toshi ba, Samsung n varos Medios..-
A Ferrari FF acaba de ser revelada. Se trata do
primeiro modelo da marca a ter trao integral.
Alm disso, ele conta com um motor dianteiro
VI 2. Se trata de um modelo GT de quatro
lugares que no s substitui a 612 mas tambm
atrai umnovo tipo de cliente, daquele que gosta
de percorrer caminhos mais difceis que exigem
trao integral.
ste modelo revolucionrio (dentro da marca)
temumnovo chassi com entre-eixos maior,
alm de suspenso independente que incorpora
a ltima gerao de amortecedores ajustveis,
alm de freios de cermica da Brembo.
linoAndrold -Todas os direitos resrvaas'
Figura 8.1 - Aplicativo dos carros executando em um smartphone Android 2.x.
<3^'.fe:Lembr-setno final do captulo 4'tambm customizamos o layout
- "para a tela large do GalaxyTab de 7. v \
i;. ; T-. s
Nosso objetivo migrar o projeto para identificar se o aparelho um tablet
com Android 3.x e exibir uma activity turbinada com os novos recursos, princi
palmente a navegao por Tabs na ActionBar.
Na primeira tela vamos exibir o dashboard na parte da esquerda, onde, ao
lado, vamos exibir a tela de sobre com um webview, conforme a figura 8.2.
Cap tulo 8 Mi gran do o apli cati vo para tablets 269
S Carros 3.0
Figura 8.2 - Tela do dashboard do aplicativo executando em um tablet com Android 3.x.
Feito isso, vamos customizar a tela que lista os carros, e para aproveitar me
lhor o espao disponvel vamos dividir a tela em duas partes, com a listagem na
esquerda e o contedo na direita.
A figura 83 exibe o aplicativo executando em um tablet, o que o objetivo
deste captulo.
Para usufrui r ao mximo os novos recursos disponveis vamos utilizar a
ActionBar, onde vamos habilitar a navegao por Tabs, e utilizar botes de ao,
como o atualizar a lista de carros. Tambm vamos dividir a tela em duas partes
utilizando fragments.
O resultado, a princpio, muito prximo daquilo que fizemos para customizar a
tela para o identificador large do GalaxyTab, conforme podemos visualizar na figura 8.4.
270
Google An droi d para T ablets
I l mbori hl nl Avenci dof ^
I D* ri (Jo
| Chevrel ec Corvef t eZOS .
^
Renault Megan* RS
Trophy
Omh Jo
Unerat l Gf fl Cbrio
Spon
DcwrifJo
MERCEOES-BENZCC3
AMG
Outro modelo que estreou em Genebra, conta 2.9 segundos euma velofldde
modelo mas raptdo e potente que a Umborj hir produou.. SuapraJuJo ter HmitaOa 14000 unidades (4099 MurcKtagos foram
prodimdajl. A Lamborghini afirma que o Aver.tadar esta duas geriSes nafrente de quafquer carro a venda, usando suspenso inspirada
em cxros da Frmula 1e chassi feitos em fibra de carbono.
Figiirn 8-3 - Aplicativo dos carros executando em um tablet com Android 3.x.
l i e 1134 mi |
I ChevroietlmpalaCoup* y
Dtscrifci
CadUUc DcvOte
Convertibl* >
Dexrt lo__________
Cht vrol et 6J-Al r .
Dwailo
O Chevrolet covene t amWm um clssico antigo,
encontrado nos satfes automobflfstcos Internacionais,
ele foi o pri meiro cano produi f do Inteiramente dos
Estados Uni dos. O mexeto de 1953. pertencente
pri meira geraJo o cofvette um dos mais
conhecidos e era uma potncia na poca em que foi
cri ado. Esse carro dssi co chegava a 170 km/ h e era
bem leve, o que esava se t omando tendncia quando
ee comeou a ser fabricado em 1953.
CaHMxf FMnmrf o
Figura 8.4-Aplicativo dos carros executando em um tablet de 7 como o GalaxyTab.
Mas, na verdade, tanto visualmente como internamente existe uma grande
diferena entre o aplicativo customizado para a tela large do GalaxyTab que im
plementamos no captulo 4, para a verso qul vamos customizar neste captulo.
Cap tulo 8 Mi gran do o apli cati vo para tablets 271
1
Desta vez vamos utilizar a nova API de Fragmentls.
Quando implementamos a tela dividida em duas partes no Galaxy Tab dei
xamos toda a lgica na activity, pois ela controla tanto a lista de carros no lado
esquerdo quanto a exibio do carro selecionado no lado direito.
Quando um carro selecionado na lista, verificado se existe o layout com
os detalhes na direita. Em caso positivo, os detalhes so exibidos nesse layout.
Lembre-se que esse comportamento ocorre apenas na horizontal, pois um layout
especfico foi criado para essa orientao.
O fato de termos que customizar o layout em XML para diversas resolues e
para a vertical e a horizontal at que simples. Foi o que fizemos ao criar as telas:
/res/carros.x ml
/res/lay out-large-land/carros.xml
O primeiro layout utilizado na vertical, e o segundo foi o que customizamos
para executar sobre o GalaxyTab na horizontal, que possui uma tela caracterizada
como large.
S para comear, para tablets com Android 3.x poderamos fazer a mesma
customizao, se necessrio, mas podemos trocar a palavra large por x large, por
exemplo, que o identificador dos novos tablets com Android 3.x.
/res/lay out-x large-land/carros.xml
Outra maneira de customizarmos essa tela para tablets com Android 3.x criar
a pasta com o identificador vil (API Levei = 11), sendo que somente aparelhos com
o identificador igual ou superior a 11iro utilizar esse layout. A seguir podemos
verificar alguns exemplos.
/res/lay out-vll/carros.x ml
/res/lay out-land-vll/carros.x ml
/res/lay out-large-land-vll/carros.x ml
/res/lay out-x large-land-vll/carros.x ml
Bom, posteriormente faremos essa customizao de layout. Mas por enquanto
podemos verificar que para o GalaxyTab seguimos o mesmo processo.
Mas note que o cdigo da activity na verso GalaxyTab faz coisas demais, pois
alm de buscar a lista de carros, ela tambm possui toda a lgica para atualizar a tela
de detalhes. Esse problema pode ser facilmente solucionado ao dividir o aplicativo
em pedaos menores e reutilizveis com fragments, que vo representar determinada
view da tela, e encapsular toda a lgica necessria para atualizar o seu contedo e
tratar os seus eventos, tirando todo o cdigo e responsabilidade da activity
272
Google An droi d para T ablets
' A vantagem de se utilizar fragments que so componentes
- reutilizveis que podem ser compartilhados entre as verses
smartphone e tablet.
No prximo tpico vamos comear a migrao!
8.2 Criando o projeto
A ltima verso que temos do aplicativo dos carros foi feita no captulo 5, onde
compilamos nosso projeto com o Android 3.x.
Para continuar podemos fazer uma cpia do projeto LivroAndroid-Cap05-Carros-
Android3 e renomear para LivroAndroid-Cap08-Carros-Tablet.
Lembre-se que o projeto que fizemos no captulo 5 foi configurado e compilado
corretamente com o Android 3.x..
Apenas para lembrar, o AndroidManifest.xml foi definido da seguinte forma:
< uses-sdk android:minSdk Version= " 4" android:targetSdk Version= ll" />
E tambm criamos um arquivo customizado de estilos para habilitar o tema
H olographic e a ActionBar conforme demonstrado a seguir.
/res/values-v11/css.xml
< resources>
< sty le name= " tema" parent= " @ android:sty le/Theme.H olo.Light" >
< item nanie=,,android:actionBarSty le,,> @ sty le/CarrosActionBar< /item>
< /sty le>
< sty le name= " CarrosActionBar" parent= @ android:sty le/W idget.H olo.Light.ActionBar" >
< iten name= ' ' android:back ground" > @ drawable/shapeJ ieader< /item>
< /sty le>
< /resources>
Estamos utilizando o Android 3.0 (API Levei = II ) como exemplo
aqui, at porque esta a base da verso que revolucionou o mundo
dos tablets. Mas recomendado compilar o projeto e deixar o
atributo targetSdkVersion apontando sempre para a ltima verso
disponvel. Na poca em que este livro estava sendo escrito o
Android 4.1 (API Levei = 16) tinha recm sido lanado. Portanto
a melhor opo seria compilar o projeto com esta verso, e usar o
atributo android:targetSdkVersion=''16".
Lembre-se que o indicador vi l indica que essa pasta um recurso
utilizado apenas para API Levei = 11ou superior.
Cap tulo 8 Mi gran do o apli cati vo para tablets 273
Se necessrio, seguindo a mesma ideia de criar a pasta Jres/values-vll, tambm
podemos alterar o nome da aplicao, criando um arquivo Ires/valnes-vll/strings.
xml customizado para Android 3.x.
Por exemplo, podemos exibir o texto Carros 3.0 no cone dos carros que fica
na tela H ome. Naturalmente, esse recurso pode ser utilizado para qualquer texto
da aplicao.
i /res / va l ues -v l l/ s tri n gs . xml
<? xml version= " 1.0" encoding= " utf-8" ? >
< resources>
< string name= " app_ name" > Carros 3.0< /string>
< /resources>
8.3 Analisando a metodologia utilizada para a migrao
Para iniciar a migrao para tablets temos algumas alternativas:
Criar uma verso completamente nova para tablets.
Criar uma nica verso utilizando fragments para reutilizar o cdigo entre
todas as activities do projeto, seja para smartphone ou tablet.
Dependendo do projeto, uma das alternativas pode ser mais vivel. Caber a voc
analisar. Por exemplo, se for necessrio separar bem as telas e criar a verso para
tablets sem alterar a verso que j est funcionando para smartphones, poderamos
criar duas activities M ainSmartphone e M ainTablets logo no incio da seguinte forma:
& Mai n .java
public class M ain ex tends LivroAndroidActivity {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
if (AndroidU tils.isAndroid_ 3_ Tablet ()) {
// Se tablets com Android 3.x ou superior
startActivity (new Intent(this,M ainTablets.class));
} else {
// Se smartphones
startActivity (new Intent(this,M ainSmartphone.class));
}
}
gO verride
protected void onResumeQ {
Google An droi d para T ablets
super. onResumeO ;
finish();
}
}
Essa tcnica muito utilizada na prtica, pois permite criar a verso para
tablets sem alterar a verso para smartphones, desde que, claro, voc no altere
nenhum layout que j existe.
Nesse caso, futuros layouts para tablets poderiam ser sempre criados na pasta
/res/layout-vll, para nunca influenciar na verso que j existe, e isso vale para todos
os recursos do projeto, como textos e imagens.
Um problema dessa abordagem que inevitavelmente em algum lugar haver
um cdigo duplicado. Por exemplo, no nosso projeto temos a activity TelaListaCarros
que j existe, mas na verso para tablets vamos utilizar Fragments para usufruir
melhor dos recursos disponveis na plataforma. Portanto, nesse momento teremos
que duplicar o cdigo entre as activities e fragments. Isso pode ser bom ou ruim,
dependendo do caso, e sempre temos que avaliar todas as possibilidades.
Mas para este livro, a fim de aprendizado vamos criar a verso para tablets
utilizando fragments, e decidimos que esses fragments sero reutilizados na
verso smartphone, para evitar cdigo duplicado. Dessa forma, medida em que
formos desenvolvendo os fragments para a verso tablets, vamos ao mesmo tempo
refatorar a verso smartphone.
Como queremos que os fragments funcionem tambm nas verses do Android
1.6 e 2.x, vamos habilitar a biblioteca de compatibilidade no projeto.
Outra definio do projeto, que nos smartphones com A ndroid 4.x que
tambm possuem a ActionBar, vamos utiliz-la da mesma maneira que na verso
para tablets.
8.4 Adicionando a biblioteca de compatibilidade no projeto
Para utilizar os fragments em verses anteriores do Android vamos adicionar a bi
blioteca de compatibilidade ao projeto. Esse processo j foi demonstrado no captulo
6, e se voc tiver alguma dvida de como faz-lo, consulte esse captulo novamente.
Depois de adicionar a biblioteca de compatibilidade ao projeto, temos que ter
certeza de que todas as activities do projeto que precisam utilizar fragments so
filhas de android.support.v4.app.F ragmentActivity .
Cap tulo 8 Mi gran do o apli cati vo para tablets 275
Portanto, vamos alterar a activity base de nosso projeto da seguinte forma:
public class LivroAndroidActivity ex tends F ragmentActivity {
Para garantir que todas as activities do projeto tambm so filhas de android.
support.v4.app.F ragmentActivity , vamos fazer com que todas agora sejam filhas de
LivroAndroidActivity , conforme demonstrado a seguir,
public class M ain ex tends LivroAndroidActivity {
public class TelaSobre ex tends LivroAndroidActivity {
public class TelaListaCarros ex tends LivroAndroidActivity {
public class TelaDetalhesCarro ex tends LivroAndroidActivity {
Confira se suas activities esto dessa forma antes de prosseguir.
Talvez voc tenha implementado em alguma das activities o mtodo onRetainNon-
Configurationlnstance() para salvar o estado dos objetos durante a troca de orientao.
A classe F ragmentActivity deixou esse mtodo como final para controlar o ciclo de
vida dos fragments, ento no podemos mais sobrescrev-lo. Dessa forma, se voc
possui esse mtodo implementado em alguma activity, necessrio remov-lo.
Depois de adicionar a biblioteca de compatibilidade no projeto, ele deve con
tinuar funcionando normalmente. No prximo tpico vamos iniciar a criao
dos fragments.
8.5 Criando a classe base para os fragments
Da mesma forma que temos a classe LivroAndroidActivity para ser base para todas
as activities do projeto, agora vamos criar uma classe LivroAndroidFragment para ser
a base para todos os fragments que vamos criar.
Essa classe vai conter a lgica para disparar as transaes da seguinte forma:o
mtodo mais interessante que vamos inserir nessa classe o startTransacao(transacao),
que j realizamos anteriormente. Dessa forma, um fragment pode iniciar uma
transao facilmente, da mesma forma que fazamos com uma activity.
& Li vroAn droi dFragmen t.java
public class LivroAndroidF ragment ex tends android.support.v4.app.F ragment {
private int progressld = R.id.progress;
protected void alert(int mensagem) {
AndroidU tils.alertDialog(getActivity (), mensagem);
}
276
Google An droi d para T ablets
// Inicia a thread
public void startTransacao(Transacao transacao) {
boolean redeOk = AndroidU tils.isNetwork Available(getActivity ());
if (redeOk) {
// Inicia a transao
TransacaoF ragmentTask task = new TransacaoF ragmentTask (this,transacao, progressld);
task .ex ecuteQ ;
} else {
// No ex iste conex o
AndroidU tils.alertDialog(getActivity (),R .string.erro_ conex ao_ indisponivel);
}
}
public void setProgressId(int progressld) {
this.progressld = progressld;
}
}
Note que esse fragment herda da classe android.support.v4.app.F ragment da biblio
teca de compatibilidade, e no do fragment nativo android.app.F ragment disponvel
para Android 3.x ou superior! Lembre-se que vamos utilizar a biblioteca de com
patibilidade porque reutilizaremos todos os fragments que vamos criar tambm
nas verses para smartphone com Android 1.6 e 2.x.
Para que o mtodo startTransacao(transacao) funcione vamos criar a classe Tran
sacaoF ragmentTask , que encapsula um Asy ncTask para executar nossas transaes em
segundo plano com uma thread separada.
T ran sacaoFragmen tT ask.java
public class TransacaoF ragmentTask ex tends Asy ncTask < Void, Void, Boolean> {
private static final String TAG = "livroandroid" ;
private final Contex t context;
private final Fragment fragment;
private final Transacao transacao;
private Ex ception ex ceptionErro;
private int progressld;
public TransacaoF ragmentTask (F ragment fragment, Transacao transacao,int progressld) {
this. contex t = fragment. getActivity Q ;
this.fragment = fragment;
this.transacao = transacao;
this.progressld = progressld;
}
0verride
Cap tulo 8 Mi gran do o apli cati vo para tablets 277
protected void onPreEx ecuteQ {
super. onPreEx ecuteQ ;
try {
// Ex ibe o ProgressBar antes de iniciar a transao
showProgressQ ;
} catch (Ex ception e) {
Log.e(TAG, e.getH essage(), e);
}
}
gO verride
protected Boolean doInBack ground(Void... params) {
try {
transacao.ex ecutarQ ;
} catch (Ex ception e) {
Log.e(TAGj e.getH essage(), e);
// Salva o erro e retorna false
this.ex ceptionErro = e;
return false;
} finally {
try {
// Ao final da transao, desliga o ProgressBar
// Sincroniz a com a thread de interface
fragment. getActivity (). runOnlliThread (
new Runnable() {
(SOverride
public void run() {
hideProgressQ ;
}
});
} catch (Ex ception e) {
Log.e(TAG, e.getM essage(), e);
}
}
// Ok
return true;
}
(SOverride
protected void onPostEx ecute(Boolean ok) {
if (ok) {
// Transao ex ecutou com sucesso
transacao.atualiz arView();
} else {
// Erro
AndroidU tils.alertDialog(contex t, "Erro: " + ex ceptionErro.getM essageQ );
}
}
278
Google An droi d para T ablets
// Ex ibe o ProgressBar
private void showProgress() {
View view = fragment.getView();
if (view != null) {
ProgressBar progress = (ProgressBar) view.findViewByld(progressId);
if (progress != null) {
progress. setVisibility (View. VISIBLE);
}
}
}
// Desliga o ProgressBar
private void hideProgress() {
View view = fragment.getViewQ ;
if (view != null) {
final ProgressBar progress = (ProgressBar) view.findViewBy ld(progressId);
if (progress != null) {
progress.setVisibility (View.INVISIBLE);
}
}
}
}
Lembre-se que todos os imports da classe F ragment devem
referenciar a classe android.support.v4.app.F ragment da biblioteca de
compatibilidade.
Depois de termos criado a classe base LivroAndroidF ragment e de termos a infra-
estrutura necessria para executar transaes com fragments, vamos continuar
nosso exemplo.
O termo transao usado no livro se refere a qualquer operao
demorada ou pesada, que necessite executar em uma thread
separada, para evitar os erros ANR (Android Not Responding).
8.6 Criando o fragment para o dashboard
O menu inicial com o dashboard utilizado na verso para smartphones e tambm
na verso para tablets, e para reutilizar o cdigo precisamos criar um fragment
com essa funcionalidade. Para isso vamos criar a classe F ragmentDashboard conforme
demonstrado a seguir.
Cap tulo 8 Mi gran do o apli cati vo para tablets 279
& Fragmen tDashboard.java
public class F ragmentDashboard ex tends F ragment implements O nClick Listener {
private Button btEsportivos;
private Button btClassicos;
private Button btLuxo;
private Button btSobre;
public View onCreateView(android.viewTLay outInflater inflater, android.view.ViewGroup Container,
Bundle savedlnstanceState) {
View view = inflater.inflate(R.lay out.fragmentj ashboard, null);
btEsportivos = (Button) view.findViewBy Id(R.id.btEsportivos);
btEsportivos.setO nClick Listener(this);
btClassicos = (Button) view.findViewById(R.id.btClassicos);
btClassicos.setO nClick Listener(this);
btLux o = (Button) view.findViewById(R.id.btLuxo);
btLux o.setO nClick Listener(this);
btSobre = (Button) view.findViewBy ld(R.id.btSobre);
btSobre.setO nClick Listener(this);
return view;
}
@ 0verride
public void onClick (View v) {
Contex t contex t = getActivity ();
boolean redeO k = AndroidU tils.isNetwork Available(contex t);
if (redeO k ) {
Intent intent = new Intent(contex t, TelaListaCarros.class);
if (v == btEsportivos) {
intent.putEx tra(Carro.TIPO, Carro.TIP0_ ESP0RTIV0S);
startActivity (intent);
} else if (v == btClassicos) {
intent.putEx tra(Carro.TIPO, Carro.TIP0_ CLASSIC0);
startActivity (intent);
} else if (v == btLuxo) {
intent.putEx tra(Carro.TIPO , Carro.TIPO _ LU X O );
startActivity (intent);
} else if (v == btSobre) {
startActivity (new Intent(contex t, TelaSobre.class));
}
} else {
AndroidU tils.alertDialog(contex t,R.string.erro_ conex ao_ indisponivel);
}
}
}
280 Google An droi d para T ablets
Para que esse fragment contenha apenas a parte da view na qual estamos
interessados, que o <Dashboardhayout>, vamos criar um novo arquivo XML de
layout / res/layout/fragment_dashboard.xml, que pode ser visualizado a seguir.
/res/layout/f ragmen t_dashboard.xml
< F rameLay out x mlns:android= " http://schemas.android.com/apk /res/android
android: lay out_width="fill_parent"
android:layout_ height= " fill_ parent>
<com. google.android.apps.iosched.ui.widget.DashboardLay out
android:lay out_ width= " match_ parent"
android: lay out_ height=" inatch_parent" >
< Button
android:id= " @ + id/btClassicos
android: lay out_width=' 'wrap_ content"
android:lay out_ height= " wrap_ content"
android:back ground= " @ null"
android: drawablePadding= " 10dp"
android:drawableTop= ' ' @ drawable/btn_ carro_ classico
android:tex t= " @ string/menu_ classicos"
android:tex tColor= " @ color/preto"
android:tex tSty le= " bold />
< Button
android:id= " @ + id/btLux o"
android:lay out_ width= " wrap_ content
android: lay out_ height= wrap_ content
android:back ground= " @ null"
android: drawablePadding= " 10dp"
android:drawableTop= " @ drawable/btn_ carro_ lux o
android:tex t= " @ string/menu_ lux on
android:tex tColor= " @ color/preto"
android:tex tSty le= " bold" />
< Button
android:id= " g+ id/btEsportivos"
android:lay out_ width= wrap_ content" i
android:lay out_ height= " wrap_ content"
android: back ground= " @ nuH "
android:drawablePadding= " 10dp"
android:drawableTop= " drawable/btn_ carro_ esportivo
android:tex t= " @ string/menu_ esportivos
android:tex tColor= " @ color/preto
android:tex tSty le= " bold" />
Cap tulo 8 Mi gran do o apli cati vo para tablets 281
< Button
android:id= " @ tid/btSobre
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:back ground= " @ null"
android:drawablePadding= " 10dp"
android:drawableTop= " @ drawable/btn_ sobre"
android:tex t= @ string/menu_ sobre"
android :tex tColor= " @ color/preto"
android:tex tSty le= " bold" />
< /com.google.android.apps.iosched.ui.widget.DashboardLay out>
< /F rameLay out>
Como criamos esse fragment para encapsular a lgica do dashboard, podemos
remover todo o tratamento de eventos da activity M ain e deix-la simples como segue:
f i i Mai n .java
public class M ain ex tends LivroAndroidActivity {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.main);
// No precisa faz er mais nada, porq ue o F ragmentDashboard faz tudo
// Estamos utiliz ando a compatibility library
}
}
Mas para isso precisamos alterar o arquivo de layout /res/layout/main.xml para
incluir o fragment do dashboard utilizando a tag < fragment> da seguinte maneira:
f i ] / res/layout/mai n .xml
<?xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android :layout_width="fill_parent"
android: lay out_height=" fill_parent"
android:back ground= " @ color/fundo"
android:gravity = " center"
android:orientation= " vertical" >
nclude lay out= " @ lay out/include_ header" />
<fragment
android:id= " @ + id/fragDashboard"
android:lay out_ width= " match_ parent"
android:lay out_ height= " 0dp"
282 Google An droi d para T ablets
android:lay out_ weight= " 1"
d a s s = br.livroandroid.carros .fragments.F ragmentDashboard" >
< /fragment>
< include lay out= @ lay out/include_ footer" />
< /LinearLay out>
Depois de fazer essa alterao o cdigo da activity M ain ficou super simples, pois
toda a lgica do dashboard foi delegada para o fragment. Futuramente vamos
reutilizar esse fragment no layout para tablets que vamos criar, e nenhum cdigo
precisar ser duplicado.
Neste momento voc pode executar o aplicativo, pois este deve continuar
funcionando da mesma forma.
A tela do inicial dos tablets vai dividir a tela em duas partes, onde na esquerda
ficar o dashboard, e na direita a tela de sobre com o webview. Mas antes de cus
tomizarmos o layout dessa tela inicial para os tablets vamos criar outro fragment,
que vai encapsular a lgica da tela de sobre e controlar o webview.
8.7 Criando o fragment para a tela de sobre
Para que a tela de sobre seja reutilizada nas verses para smartphone e tablets
precisamos criar um fragment para encapsular essa funcionalidade.
Portanto, vamos mover o cdigo que est na classe TelaSobre para uma nova
classe F ragmentSobre que vamos criar, conforme demonstrado a seguir.
Fragmen tSobre.java
public class F ragmentSobre ex tends F ragment {
private static final String U RL_ S0BRE = "http://www.livroandroid.com.br/livro/carros/sobre.htm";
private W ebView webview;
(SOverride
public View onCreateView(Lay outInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment_sobre, null);
webview = (Webview) view.findViewBy Id(R.id.webview);
W ebSettings settings = webview.getSettings();
// Ativa o JavaScript na pgina
settings.set3avaScriptEnabled(true);
// Publica a interface para o J avaScript
webview.addDavascriptInterface(this, " LivroAndroid" );
// Configura o webview
monitoraW ebView(webview);
view.setLay outParams(new Lay outParams(Lay outParams.M ATCH _ PARENT,Lay outParams.M ATCH _ PARENT));
( Cap tulo 8 Mi gran do o apli cati vo para tablets
283
return view;
}
gO verride
public void onActivity Created(Bundle savedlnstanceState) {
super. onActivity Created(savedlnstanceState);
// Abre a U RL da pgina de sobre
webview. loadllrl(URLJjOBRE);
}
private void monitoraW ebView(W ebView webview) {
webview.setW ebViewClient(new W ebViewClient() {
gO verride
public void onPageStarted(W ebview webview, String uri, Bitmap favicon) {
super.onPageStarted(webview, uri, favicon);
// Liga o progress
ProgressBar progress = (ProgressBar) getView().findViewBy Id(R.id.progress);
progress.setVisibility (View. VISIBLE);
}
Override
public void onPageF inished(W ebView webview, String uri) {
super.onPageF inished(webview, uri);
// Apaga o boto voltar
if (AndroidJ tils. isAndroid_ 3_ Tablet(getActivity ())) {
// Apaga o voltar no tablet somente para demonstrar
apagaBotaoVoltarQ ;
}
// Desliga o progress
ProgressBar progress = (ProgressBar) getView().findViewBy Id(R.id.progress);
progress.setVisibility (View.INVISIBLE);
}
});
}
public void apagaBotaoVoltarQ {
String j sApagar = " document.getElementBy Id(,voltar,).sty le.display = ' none' ;" ;
webview.loadU rl(" j avascript:" + j sApagar + ";");
}
}
O arquivo /res/layout/fragment_sobre.xml de layout desse fragment vai conter
apenas o webview, conforme demonstrado a seguir.
d /res/layout/f ragmen t_sobre.xml
< ? xml version= " 1.0" encoding=' 'utf-8?>
< F rameLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android: layout_width="fill j arent"
284
Google An droi d para T ablets
android: lay out_ height= " fill_ parent >
< W ebView
android:id= " @ + id/webview" .
android:layout_ width= "fill_ parent"
android:lay out _height=''fill_parent" />
< ProgressBar
android:id= " @ + id/progress"
android:lay out_ width= " wrap_ content
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = " center" />
< /F rameLay out>
Como o boto voltar que existe na pgina HTML no tem utilidade na verso
para tablets, porque no existe para onde voltar, podemos executar um J avaScript
para remov-lo diretamente no webview. O mtodo apagaBotaoVoltar() dentro dessa
classe faz exatamente isso.
public void apagaBotaoVoltarQ {
String j sApagar = " document.getElementBy Id(,voltar' ).sty le.display = ,nonej ,'j
webview.loadU rl(" j avascript:" + j sApagar +
}
Lembre-se que na verso smartphone fizemos o contrrio. Deixamos esse
boto na pgina e interceptamos o seu evento dentro do cdigo J ava para fazer o
finish() da activity de sobre.
Enfim, esse boto de voltar foi adicionado na pgina web apenas para explicar
alguns conceitos interessantes que temos no webview, como interceptar eventos e
ainda executar funes J avaScript diretamente nele. Lembre-se que em projetos reais
recomenda-se seguir as boas prticas de interface e navegao do Android, e uma
delas utilizar o boto voltar nativo para fechar uma tela e retroceder anterior.
Como agora existe um fragment que encapsula a lgica de controle do web
view, podemos remover toda a lgica da activity TelaSobre e deix-la bem simples,
conforme demonstrado a seguir.
1*1 T elaSobre.java
public class TelaSobre ex tends LivroAndroidActivity {
(SOverride .
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.sobre);
}
Cap tulo 8 Mi gran do o apli cati vo para tablets 285
Para isso vamos incluir o fragment no arquivo de layout /res/values/sobre.xml
da activity
& /res/layout/sobre.xrri l
< ? xml version= " 1.0encoding= " utf-8" ? >
< LinearLay out x mlns: android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android: lay out_width=" fill_parent"
android:layout_ height="fill _ parent"
android:gravity = " center"
android:background="| lcolor/fundo"
>
< include lay out= " @ lay out/include_ header" />
<fragment
android:layout_ width="fill_parent"-
android:lay out_ height= " 0dp"
android:lay out_ weight= " l"
class= " br.livroandroid.carros.fragments.F ragmentSobre"
/>
< include lay out= " @ lay out/indude_ footer" />
</LinearLay out>
Note que os fragments sempre encapsulam determinada lgica e representam
parte da tela, deixando o cdigo da activity bem simples. Mas a grande vantagem
de criar fragments que agora podemos reutiliz-los na verso para tablets com
o mnimo de esforo.
Ao final dessa alterao execute o projeto novamente, e este deve continuar
executando da mesma forma.
8.8 Dividindo a tela do dashboard em duas partes nos tablets
A primeira alterao que vamos fazer na verso para tablets dividir a tela do
dashboard em duas partes, onde na esquerda ficar o fragment com o dashboard,
e na direita o fragment com o webview para exibir a pgina de sobre.
Atualmente, a verso para tablets est utilizando o mesmo layout para smartphone,
conforme a figura 8.5.
Nosso objetivo dividir a tela em duas partes para reaproveitar os fragments
que criamos anteriormente. Para customizar essa tela vamos criar um novo ar
quivo main.xml na pasta /res/layout-large-land-vll/ conforme demonstrado a seguir.
5yf'.V-T- ''Vfft..- V - ? ^ :
Figiira 8.5 - Tela do dashboard atual executando em um tablet
(Si / r e s / l a y o u t - l a r g e - l a n d - v n / m a i n . x m l
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns: android= " http://schemas.android.com/apk /res/android"
android:orientation= " horiz ontal"
android: lay out_ width= niatch_parent"
android: lay out_ height= rnatch_parent"
android:back ground= " # ffffff"
>
<! -- Bloco 1 -->
< LinearLay out
android:id= " @ + id/lay outl"
android:orientation= " vertical"
android:lay out_ width= " 0dp"
android:lay out_ height= " match_ parent"
android:lay out_ weight= " l"
android:lay out_ margin= " 10dp"
android:gravity = " center"
android:padding= " 2dp
android:back ground= " @ drawable/box _ fundo"
>
< fragment
android:id= " @ + id/fragDashboard"
Cap tulo 8 Mi gran do o apli cati vo para tablets
287
android:lay out_ width= " match_ parent"
android:lay out_ height= " match_ parent"
class= " br.livroandroid.carros.fragments.F ragmentDashboard" />
< /LinearLay out>
< LinearLay out
android:id= " @ + id/lay outF ragSobre"
android:orientation= " vertical"
android:lay out_ width= " 0dp"
android:lay out_ height= " match_ parent"
android:lay out_ margin= " 10dp
android:lay out_ weight= " l"
android:gravity = " center"
android:back ground= " @ drawable/box _ fundo"
< fragment
android:id= " g+ id/fragSobre"
android: lay out_ width=' 'match_parent"
android:lay out_ height= " match_ parent"
class= " br.livroandroid.carros.fragments.F ragmentSobre/>
< /LinearLay out>
< /LinearLay out>
A figura 8.6 exibe a pr-visualizao desse arquivo no editor visual do Eclipse.
, maiaxml tZ
Editing-eeftfigi Lafge & v l awdacapc Ori<nmio<vA PH <
t .i svLa rge Mdi um vn* i |;
But nxi - off j v Owt kf l anj!
Ri doB ucton t sVI* w l;
o -
Figura 8.6 - Pr-visualizao da tela do dashboard para tablets dividida em duas partes.
I
Note que estamos utilizando a pasta /res/layout-large-land-vll, que utilizada
por aparelhos com API Levei = 11ou superior, e tambm estamos criando esse
arquivo somente para a horizontal. Tambm poderamos ter utilizado o prefixo
x large para as grandes telas, mas a vantagem de se utilizar o prefixo large que
o mesmo arquivo de layout pode ser usado na Google TV E para este simples
projeto isso atende s necessidades.
Repare que agora que os fragments existem, extremamente simples de se
reaproveitar o cdigo! Feito isso, as classes F ragmentDashboard e F ragmentSobre faro
todo o trabalho, e a activity M ain nem sequer precisa ser alterada.
<fragrrent
android:id=" tid/fragDashboard'1
android:lay out_ width= " match_ parent"
android:lay out_ height= " match_ parenf
class= " br.livroandroid.carros.fragments.FragmentDashboard"
/>
< fragment
android:id= " ^ tid/fragSobre"
android:lay out_ K idth= " match_ parent"
android:lay out_ height= " match_ parent
dass= " br.livroandroid.carros.fragments.F ragmentSobre"
/>
Para finalizar a explicao desse layout, repare que ambos os layouts, o da
esquerda e o da direita, utilizam uma imagem Nine Patch como plano de fundo,
para criar uma borda arredondada.
< LinearLay out
android:id= " @ + id/lay outl"
android:back ground= " @ drawable/box _ fundo"
>
A vantagem de utilizar esse tipo de imagem que ela pequena, e partes
dela so marcadas para esticar conforme o tamanho final da view. Existe uma
ferramenta chamada Draw 9-patch que permite criar essas imagensJ ara maiores
detalhes consulte a documentao oficial.
Essa imagem pode ser encontrada no projeto com os downloads do livro na
seguinte pasta: /res/drawable-mdpi/box_Jundo.9.png. Depois de copi-la atente-se
para configurar corretamente ambos os blocos, da esquerda e da direita, com essa
imagem de fundo, para criar o efeito com a borda arredondada na view.
Agora podemos executar o projeto, e o resultado deve ser similar figura 8.7.
288 Google An droi d para T ablets
Cap tulo 8 Mi gran do o apli cati vo para tablets 289
Se voc chegou at aqui, parabns, pois acabou de reutilizar os fragments entre
a verso para smartphones e tablets, e agora a manuteno do cdigo-fonte do
projeto ficar bem mais simples.
Figura 8.7 - Tela do dashboard para tablets dividida em duas partes
Repare que na ActionBar aparece o texto Carros 3.0, que o nome da aplica
o e que customizamos anteriormente com o arquivo Ires/values-vll/strings.xml.
(Si /res/values-vl 1/stri n gs.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
<resources>
< string name= " app_ name" > Carros 3.0< /string>
< /resources>
8.9 Tratando o layout diferenciado para tablets
Como dividimos a tela do dashboard em duas partes na verso para tablets, temos
que tratar da existncia ou no do layout do fragment de sobre na direita da tela.
Como nos tablets a pgina de sobre ser exibida juntamente com o dashbo
ard, o boto de sobre no tem muita utilidade nessa tela, e fica estranho abrir a
activity TelaSobre ao clicar nesse boto se a pgina de sobre j est sendo exibida
direita da tela. i
290
Google An droi d para T ablets
Portanto, vamos inserir o seguinte cdigo na classe F ragmentDashboard para con
trolar se existe ou no a necessidade de exibir a activity, que o caso da verso
para smartphones:
// No tablet 3.x pode ser que o F ragmentSobre j estej a na tela
View lay outF ragSobre = getActivity ().findViewBy Id(R.id.lay outF rag5obre);
if (lay outF ragSobre == null) {
// Se no ex istir, significa q ue o lay out para smartphones
startActivity (new Intent(contex t, TelaSobre.class));
}
A seguir podemos verificar uma verso resumida do cdigo-fonte do F ragmen
tDashboard com essa alterao.
i d Fragmen tDashboard.java
public class F ragmentDashboard ex tends LivroAndroidF ragment implements O nClick Listener {
(Sverride
public void onClick (View v) {
Contex t contex t = getActivity Q ;
boolean redeO k = AndroidU tils.isNetwork Available(contex t);
if (redeO k) {
Intent intent = new Intent(contex t,TelaListaCarros.class);
if (v == btEsportivos) {
} else if (v == btClassicos) {
} else if (v == btLuxo) {
} else if (v == btSobre) {
// No tablet 3.x pode ser q ue o F ragmentSobre j estej a na tela
View lay outF ragSobre = getActivity Q .findViewBy Id(R.id.lay outF ragSobre);
if (lay outF ragSobre == null) {
// Se no existir, significa q ue o lay out para smartphones
startActivity (new Intent(contex t, TelaSobre.class));
}
}
} else {
AndroidU tils.alertDialog(contex t,R.string.erro_ conex ao_ indisponivel);
}
}
}
Cap tulo 8 Mi gran do o apli cati vo para tablets
&
8.10 Criando o fragment para a tela de detalhes do carro
A tela do dashboard j est funcionando nos tablets. O prximo passo dividir
em duas partes a tela que faz a listagem dos carros e habilitar a navegao por
Tabs na ActionBar.
Mas antes disso temos que criar o fragment que vai exibir os detalhes do carro,
porque vamos utilizar esse fragment no lado direito da tela na verso para tablets.
Na verso para smartphones esse fragment vai conter a lgica que tem na
activity TelaDetalhesCarro, a qual tambm passar a utilizar esse fragment.
Ento mos obra! A seguir podemos visualizar o cdigo-fonte da classe
F ragmentDetalhesCarro.
Fragmen tDetalhesCarro.java
public class F ragmentDetalhesCarro ex tends LivroAndroidF ragment implements O nClick Listener { i
private static final String TAG = " livroandroid";
private Carro carro;
gO verride
public View onCreateView(Lay outInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.lay out.fragment_carro_detalhes, null);
// No q ueremos a barra do header aqui
View lay outH eader = view.fndViewBy ld(R.id.lay outH eader);
if (lay outH eader != null) {
lay outH eader.setVisibility (View.GONE);
}
// Abrir o site
Button btSite = (Button) view.findViewByld(R.id.btAbrirSite);
btSite.setO nClick Listener(this);
view.setLay outParams(new Lay outParams(Lay outParams.M ATCH _ PARENT,Lay outParams.M ATCH _ PARENT));
return view;
}
gO verride
public void onActivity Created(Bundle savedlnstanceState) {
super.onActivity Created(savedlnstanceState);
Bundle args = getArguments();
if (args != null) {
// Recebe o carro selecionado e atualiz a os detalhes
carro = (Carro) args.getSerializ able(Carro.K EY );
updateView();
}
}

F
C
E


C
A
M
P
U
S

F
O
s
U

A
L
E
2
A

5
B
I
B
L
I
O
T
E
C
A
292
Google An droi d para T ablets
// Atualiz a os detalhes do carro
private void updateViewQ {
View view = getViewQ ;
Log.i(TA6j " Ex ibindo carro: " + carro.nome);
Tex tView tH eader = (Tex tView) view.findViewBy ld(R.id.tHeader);
Tex tView tDesc = (Tex tView) view.findViewByld(R.id.tDesc);
if (tH eader != null) {
tH eader.setTex t(carro.nome);
}
tDesc.setTex t(carro.desc);
// L a imagem do cache
final ImageView img = (ImageView) view.findViewBy ld(R.id.img);
CarrosApplication application = (CarrosApplication) getActivity ().getApplication();
DownloadlmagemU til downloader = application.getDownloadImagemU til();
Bitmap bitmap = downloader.getBitmap(carro.urlF oto);
if (bitmap != null) {
img.setlmageBitmap(bitmap);
}
}
(SOverride
public void onClick (View view) {
String uri = carro.urllnfo;
startActivity (new Intent(Intent.ACTIO N_ VIEW , U ri.parse(url)));
}
}
Esse fragment vai utilizar o seguinte arquivo de layout, que antes ficava no
layout da activity:
/res/layout/f ragmen t_carro_detalhes.xml
< ? x ml version= 1.0" encoding= utf-S" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:lay out_ width= fill_parent"
android: lay out_ height= fill_ parent"
android:orientation= " vertical" >
< ImageView
android:id= " g+ id/img"
android: lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = " center" />
< ScrollView
android:lay out_ width= " match_ parent"
android:lay out_ height= " wrap_ content" >
Cap tulo 8 Mi gran do o apli cati vo para tablets
293
< LinearLay out
android:lay out_ width= " match_ parent"
android:lay out_ height= " wrap_ content"
android:gravity = " center" *
android:orientation= " vertical"
android:padding= " 6dp" >
< View
android: layout_ width= ''match_ parent"
android:lay out_ height= ldp"
android:lay out j narginBottom= " 6dp"
android:lay out_ marginTop= " 6dp"
android:back ground= " # dddddd" />
Tex tView
android:id= " @ + id/tDesc"
sty le= |8style/textol4"
android:layout_ width=''fill_parent"
android: layout_height=" fill_parent"
android:lay out_ weight= " l" />
<Button
android: id= "(8+id/btAbrirSite"
android: lay out_ width= ' ' wrap_ content"
android:lay out_ height= wrap_ content"
android: lay outj nargin= " 4dp"
android:back ground= " gdrawable/btn_ normal"
android:tex t= @ string/abrirSite"
android:tex tColor= " @ color/preto" />
< /LinearLay out>
</5crollView>
< /LinearLay out>
Como agora o fragment novamente vai implementar todo o trabalho pesado,
podemos alterar o layout da activity para incluir esse fragment da seguinte maneira:
i f e /res/1 ay o ut/ca rro_detalhes.xmI
< ? xml version= 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:andcaid= " http://schemas.android.com/apk /res/android
android:lay out_ width= fill_parent"
android:lay out_height=" fill_parent"
android:back ground= " @ color/fundo"
android:orientation= " vertical" >
<include lay out= " @ lay out/include_ header" />
< LinearLay out
android: id='' @+id/lay out
294
Google An droi d para T ablets
android:lay out_width= " fill_ panent"
android:lay out_ height= " 0dp"
android:lay out_ weight= " l" >
<! -- fragment aqui -->
< /LinearLay out>
< include lay out= " @ lay out/include_ footer" />
< /LinearLay out>
Note que no deixamos a tag < fragment> fixa no layout porque necessrio passar
um parmetro para o fragment, que o carro selecionado, e isso somente possvel de
fazer se inserirmos o fragment dinamicamente no layout com uma F ragmentTransaction.
Lembre-se que para abrir a activity TelaDetalhesCarro a seguinte Intent utilizada,
passando o objeto Carro como parmetro;
Intent intent = new Intent(this, TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , c);
startActivity (intent);
Dessa forma, precisamos passar esse Bundle com os parmetros para o F ragmentDe
talhesCarro tambm, e para isso vamos alterar o cdigo da activity TelaDetalhesCarro.
i s> T elaDetalhesCarro.java
public class TelaDetalhesCarro ex tends LivroAndroidActivity {
gO verride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carro_ detalhes);
// No faz mais nada porq ue o F ragmentDetalhesCarro faz tudo
if (savedlnstanceState == null) {
// Insere somente a primeira vez
// porq ue a F ragmentTransaction persiste durante a troca de orientao
F ragmentDetalhesCarro fragDetalhes = new F ragmentDetalhesCarro();
// Passa os parmetros para o fragment, porq ue o bundle cont m o carro,
// por isso estamos utiliz ando uma F ragmentTransaction, pois pelo X M L no possvel
// passar os parmetros
Bundle args = getIntent().getEx tras();
fragDetalhes.setArguments(args);
F ragmentTransaction t = getSupportF ragmentM anager().beginTransaction();
t.add(R.id.lay out, fragDetalhes);
t.commit();
}
Cap tulo 8 Mi gran do o apli cati vo para tablets 2 5

Note que o cdigo da activity simples e apenas utiliza uma F ragmentTransaction


para adicionar o F ragmentDetalhesCarro no layout. Feito isso, o fragment far todo o
trabalho!
Na verso smartphone, s vezes chato fazer isso e encapsular a lgica nos frag
ments, mas seremos recompensados posteriormente quando formos implementar a
verso para tablets, onde vamos reutilizar esse fragment com o mnimo de esforo.
Depois dessa alterao execute o projeto novamente. Ele dever continuar
funcionando tanto nos smartphones quanto nos tablets.
8.11 Criando o fragment para a tela de listagem de carros
Antes de finalizarmos a migrao para tablets, existe um ltimo fragment que
precisamos criar, o qual vai encapsular o cdigo que controla a lista de carros.
Portanto, vamos criar a classe F ragmentListaCarros, que conter uma lgica pare
cida com a atual activity TelaListaCarros, da seguinte forma:
i =i Fragmen tLi staCarros.java
public class FragmentListaCarros extends LivroAndroidFragment implements OnltentlickListener, Transacao {
private static final String TAG = " livroandroid" ;
protected Listview listview;
protected List< Carro> carros;
protected String tipo;
(SOverride
public View onCreateView(Lay outInflater inflater, ViewGroup Container, Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment_carros, null);
Bundle args = getArguments ();
if (args != null) {
tipo = args.getString(Carro.TIPO );
}
if (tipo = = null) {
tipo = Carro.TIP0_ LU X 0;
}
listview = (Listview) view.findViewById(R.id.listview);
listview.setO nltemClick Listener(this);
// Configura o id do ProgressBar para ser ligado/desligado ao abrir a transao
setProgressId(R.id.progressLista);
view. setLay outParams(new Lay outParams (Lay outParams ,HATCH _PARENT, Lay outParams .M ATCH _PARENT));
return view;
}
296
Google An droi d para T ablets
gO verride
public void onActivity Created(Bundle savedlnstanceState) {
super. onActivity Created(savedlnstanceState);
if (savedlnstanceState != null) {
// Recuperamos a lista de carros salva pelo onSavelnstanceState(bundle)
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
Log.i(TAG," Lendo estado: savedlnstanceState(carros)" );
this.carros = lista.carros;
}
// Se a lista de carros existe, atualiz a a lista, seno dispara a busca
if (carros != null) {
atualiz arView();
} else { 1
startTransacao(this);
}
}
gO verride
public void onSaveInstanceState(8undle outState) {
super.onSavelnstanceState(outState);
Log.i(TAG," Salvando Estado: onSavelnstanceState(bundle)" );
// Salvar o estado da tela
outState.putSerializ able(ListaCarros.K EY , new ListaCarros(carros));
}
(SOverride
public void ex ecutar() throws Ex ception {
// Busca os carros em uma thread
this.carros = Carro5ervice.getCarros(getActivity (), tipo);
}
(SOverride
public void atualiz arViewQ {
// Atualiz a os carros na thread principal
if (carros != null & & !carros.isEmpty () & & getActivity Q != null) {
// Atualiz a o ListView
listView.setAdapter(new CarroAdapter(getActivity (), carros));
}
}
(SOverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapterQ .getltem(posicao);
visualiz arDetalhes(c);
}
// Ex ibe os detalhes do carro
private void visualiz arDetalhes(Carro carro) {
Cap tulo 8 Mi gran do o apli cati vo para tablets 297
// Passa o carro selecionado por parmetro
Intent intent = new Intent(getActivity (), TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , carro);
start Activity (intent);
}
}
Esse fragment vai utilizar o seguinte arquivo de layout, que contm o Listview
com a lista de carros:
& /res/layout/f ragmen t_carros.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< F rameLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:lay out_ width= " match_ parent"
android: lay out_ height= " match_ parent"
android:back ground= " @ color/branco"
android: gravity=''center"
android:padding= " 2dp >
< Listview
android: id="|9+id/listview"
android:lay out_ width= " match_ parent"
android:lay out_ height= " match_ parent"
android:cacheColorH int= " @ color/transparente" />
< ProgressBar
android:id= " @ + id/progressLista"
android:lay out_ width= wrap_ content
android:lay out_ height= " wrap_ content"
android:lay out_ gravity = " center"
android:visibility = " invisible" />
< /F rameLay out>
Depois de criarmos esse fragment removeremos a lgica da activity TelaLis-
taCarros, que agora pode delegar toda a responsabilidade para o fragment que
criamos. Para isso vamos adicionar o fragment no layout da activity utilizando
uma F ragmentTransaction.
i T elaLi staCarros.java
public class TelaListaCarros ex tends LivroAndroidActivity {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.layout.carros);
298
Google An droi d para T ablets
// No faz mais nada porq ue o F ragmentDetalhesCarro faz tudo
if (savedlnstanceState == null) {
// Insere somente a primeira vez
// porque a F ragmentTransaction persiste durante a troca de orientao
FragmentListaCarros fragLista = new F ragmentListaCarros();
// Passa os parmetros para o fragment, q ue cont m o tipo do carro selecionado,
// por isso estamos utiliz ando uma transao, pois pelo X M L no
// possvel passar os parmetros
Bundle args = getIntent().getEx tras();
fragLista.setArguments(args);
F ragmentTransaction t = getSupportF ragmentM anager().beginTransaction();
t.add(R.id.lay outEsq uerda, fragLista);
t.commit();
}
}
}
Para essa classe compilar, precisamos atualizar o layout da activity para deixar
o espao reservado para o fragment, que ser inserido dinamicamente.
i /res/layout/carros.xml
< ? xml version= " 1.0encoding= " utf-8" ? >
(LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android: layout_width=" fill_parent"
android:lay out_ height="fill_parent"
android:gravity = center"
android:back ground= " @ color/fundo"
>
nclude layout=" lay out/include_ header/>
(LinearLay out
android: id= +id/lay outEsq uerda"
android: lay out_width=" fill_parent"
android:lay out_ height= " 0dp"
android:lay out_ weight= l"
>
<!-- F ragment aq ui -->
< /LinearLay out>
cinclude lay out= " layout/include_footer" />
< /LinearLay out>
Cap tulo 8 Mi gran do o apli cati vo para tablets
299
No layout /res/layout/carros.xml utilizado para smartphones foi
dado o identificador @+id/layoutEsqnerda para compatibilizar
o identificador desse layout com a verso para tablets que vamos
fazer logo a seguir. Para os tablets vamos dividir esse layout em duas
partes, onde teremos os layouts @+id/layoutEsquerda com a lista de
carros e o @+idJlayoutDireita com os detalhes.
Pronto, agora toda a verso smartphone j foi refatorada para utilizar os frag
ments. Nesse momento voc pode executar novamente a aplicao, e ela deve
continuar funcionando da mesma forma.
Vale lembrar que o cdigo que criamos para a classe LivroAndroidActivity , que
a activity me de todas as activities do projeto, tinha mtodos utilitrios, e o
principal deles era o startTransacao(transacao). Mas agora, como toda a lgica fica
nos fragments, todo o cdigo dessa activity tambm pode ser removido. A seguir
podemos visualizar o cdigo-fonte completo dessa classe.
(Sn Li vroAn droi dActi vi ty.java
import android.support.v4.app.FragmentActivity ;
public class LivroAndroidActivity ex tends F ragmentActivity {
// Nada aqui, porq ue os fragments faz em tudo
// M as lembre-se q ue para a compatibility library funcionar
// necessrio herdar de F ragmentActivity
}
At este momento estamos preparando os fragments que vamos reutilizar, e
para isso estamos refatorando o cdigo da verso smartphone. Isso pode ser bom
ou ruim, dependendo do projeto, e caber a voc analisar. Neste livro estamos
seguindo essa abordagem a fim de explicar bem o assunto e mostrar na prtica
como reutilizar os fragments. Lembre-se que outra abordagem seria utilizar um
cdigo diferente entre as verses smartphone e tablet.
Agora vamos continuar com a migrao e criar o layout dividido em duas
partes na tela de listagem de carros para os tablets.
8.12 Criando o layout para a listagem de carros para tablets
Agora que temos todos os fragments que exibem a lista de carros e os detalhes
criados, podemos criar um layout otimizado para tablets e reaproveitar esses
fragments.
300
Google An droi d para T ablets
Para isso vamos criar o arquivo IresIlayout-large-lan-vUlcarros.xml conforme de
monstrado a seguir.
( d * /res / I ay out- l arge -l a n d-v l l/ c arr os. x ml
< ? x ml version= n1.0" encoding= " utf-8" ? >
(LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:lay out_ width= match_ parent"
android:lay out_ height= matchj iarent"
android:back ground= " @ color/fundo"
android:gravity = center
android:orientation= " horiz ontal" >
<!-- Bloco Esq uerda -->
(LinearLay out
android:id= "|9tid/layoutEsquerda"
android:lay out_ width= 0dp"
android:lay out_ height= " match_ parent"
android:lay out_ margin= " 6dp
android:lay out_ weight= " 3
android: background="(5)drawable/box_fundo"
android:orientation= " vertical" >
< ! F ragment q ue lista os carros aq ui -->
(/LinearLay out)
(! -- Bloco Direita -->
(LinearLay out
android: id= "(3+id/layoutDireita"
android: lay out_ width= " 0dp"
android:lay out_ height= " match_ parent
android:lay out_ margin= " 6dp"
android:lay out_ weight= " 8
android:back ground= @ drawable/box _ fundo
android:orientation= " vertical" >
(!-- F ragment com os detalhes do carros aqui -->
(/LinearLay out)
(/LinearLay out)
A figura 8.8 exibe a pr-visualizao desse arquivo de layout no Eclipse, onde a
tela est dividida em duas partes.
Cap tulo 8 Mi gran do o apli cati vo para tablets
301
Figura 8.8 - Layout dos cairos para a tela dos tablets.
Depois que criamos esse layout podemos executar novamente o projeto, e desta
vez a verso para tablets j vai exibir o fragment com a listagem na esquerda da
tela, conforme a figura 8.9.
Figura 8.9 - Tela com a lista de carros dividida em duas panes para tablets.
302 Google An droi d para T ablets
&
8.13 Utilizando o fragment de detalhes na direita
Neste momento a verso para tablets j est exibindo o fragment que exibe a
listagem de carros na esquerda, mas ao selecionar algum carro da lista a activity
TelaListaCarros ser executada para exibir os detalhes.
I sso acontece porque ainda no estamos tratando a existncia do novo layout
para tablets e estamos apenas iniciando a nova activity.
(SOverride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapterQ .getltem(posicao);
visualiz arO etalhes(c);
}
// Ex ibe os detalhes do carro
private void visualiz arDetalhes(Carro carro) {
// Passa o carro selecionado por parmetro
Intent intent = new Intent(getActivity (), TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , carro);
startActivity (intent);
}
Para solucionar esse problema vamos atualizar esse cdigo para utilizar o
F ragmentDetalhesCarro da seguinte forma:
(Note que o carro selecionado passado como parmetro para o fragment
de detalhes.)
F ragmentDetalhesCarro fragDetalhes = new F ragmentDetalhesCarro();
Bundle bundle = new Bundlef);
bundle.putSerializ able(Carro.K EY , carro);
fragDetalhes.setArguments(bundle);
F ragmentTransaction ft = getF ragmentH anager().beginTransaction();
f t.replace(R.id.layoutDireita, fragDetalhes);
ft.commit();
O cdigo-fonte completo e atualizado da classe F ragmentDetalhesCarro pode ser
visualizado a seguir.
& Fragmen tLi staCarros.java
public class FragmentListaCarros extends LivroAndroidFragment iplements OnltemClickListener, Transacao {
private static final String TAG = " livroandroid" ;
protected ListView listView;
protected List< Carro> carros;
protected String tipo;
gO verride
public View onCreateView(Lay outInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment_carros, null);
Bundle args = getArguments();
if (args 1= null) {
tipo = args.getString(Carro.TIPO );
}
if (tipo == null) {
tipo = Carro.TIPO J .U X O ;
}
listView = (ListView) view.findViewBy ld(R.id.listview);
listview.setO nltemClick Listener(this);
// Configura o id do ProgressBar para ser ligado/desligado ao abrir a transao
setProgressId(R.id.progressLista);
view.setLay outParams(new Lay outParams(Lay outParanis.H ATCH _ PARENT,Lay outParams.H ATCH _ PARENT));
return view;
}
gO verride
public void onActivity Created(Bundle savedlnstanceState) {
super.onActivity Created(savedlnstanceState);
if (savedlnstanceState != null) {
// Recuperamos a lista de carros salva pelo onSavelnstanceState(bundle)
ListaCarros lista = (ListaCarros) savedlnstanceState.getSerializ able(ListaCarros.K EY );
Log.i(TAG," Lendo estado: savedlnstanceState(carros)" );
this.carros = lista.carros;
}
// Se a lista de carros ex iste, atualiz a a lista, seno dispara a busca
if (carros != null) {
atualiz arViewQ ;
} else {
startTransacao(this);
}
}
gO verride
public void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
Log.i(TAG," Salvando Estado: onSavelnstanceState(bundle)" );
// Salvar o estado da tela
outState.putSerializ able(ListaCarros.K EY , new ListaCarros(carros));
}
gO verride
public void executar(). throws Ex ception {
// Busca os carros em uma thread
Cap tulo 8 Mi gran do o apli cati vo para tablets ^ 30.
304
Google An droi d para T ablets
this.carros = CarroService.getCarros(getActivity (), tipo);
}
lverride
public void atualiz arViewQ { *
// Atualiz a os carros na thread principal
if (carros != null SS !carros.isEmpty () 8& getActivity O != null) {
// Atualiz a o Listview
listview.setAdapter(new CarroAdapter(getActivity (), carros));
// Recupera o primeiro carro da lista para ex ibir os detalhes
Carro c = carros.get(e);
visualiz arDetalhes(c, false);
}
}
@ O verride
public void onItemClick (AdapterView< ? > parent, View view, int posicao,long id) {
Carro c = (Carro) parent.getAdapter().getItem(posicao);
visualiz ar etalhes(c, true);
}
// Ex ibe os detalhes do carro
private void visualiz arDetalhes(Carro carro, boolean ex ibirDetalhes) {
F ragmentDetalhesCarro fragDetalhes = new F ragmentDetalhesCarro();
// Passa o carro selecionado por parmetro
Bundle bundle = new BundleQ ;
bundle.putSerializ able(Carro.K EY , carro);
fragDetalhes.setArguments(bundle);
View lay outDireita = getActivity ().findViewById(R.id.layoutDireita);
if (lay outDireita != null) {
// Atualiz a o fragment no layout da direita
F ragmentTransaction ft = getF ragmentH anager().beginTransaction();
ft.replace(R.id.layoutDireita, fragDetalhes);
ft.commitO ;
} else {
// Seno inicia uma nova activity
if (exibirDetalhes) {
// Somente navega para prx ima tela se algum carro foi selecionado
// No em caso da primeira req uisio
Intent intent = new Intent(getActivity (),~ TelaDetalhesCarro.class);
intent.putEx tra(Carro.K EY , carro);
startActivity (intent);
}
Com essa alterao esse fragment ser esperto e utilizar o layout da direita
na verso para tablets. Mas na verso para smartphone uma activity ser iniciada
para exibir a tela de detalhes.
Note que tambm inserimos um controle nesse fragment para carregar auto
maticamente o primeiro carro da lista logo na primeira vez, para que a parte da
direita no fique vazia.
Feita essa alterao, podemos executar novamente o projeto, desta vez a verso
para tablets j vai exibir corretamente os dois fragments, na esquerda e na direita
da tela, conforme a figura 8.10.
Cap tulo 8 Mi gran do o apli cati vo para tablets 305
modelo masrapl doe polente que a lamborghini produou.. Sua produAoseri l i mrj dj * 4000 unidades (4099 Muiolwos foram
produzi dal . A lamborghini afirma que o Averador t sJ due graes na frenle de c^utquer carro a venda, usando suspendo Inspirada
--------------. . . . . . . ........^ utbOfiO.
henaul i Mej ant RSTrophy
AR*na4ttp<n*ntoj nu
Figura 810 - Lista de carros funcionando com fragments na verso tablet.
8.14 Navegao porTabs na ActionBar para tablets
Averso para tablets j est funcionando com fragments, e agora vamos turbinar
a ActionBar e habilitar a navegao por Tabs para escolher os tipos dos carros.
Mas para isso vamos utilizar diretamente a API da AcrionBar, portanto vamos
criar outra activity exclusiva para tablets.
J existe a activity TelaListaCarros para smartphones, e agora vamos criar uma
nova activity chamada TelaListaCarrosTablets, exclusiva para tablets.
306
Google An droi d para T ablets
Como j fizemos exerccios sobre Tabs e ActionBar, vamos exibir o cdigo-fonte
completo de uma vez.
( d T elaLi staCarrosT ablet.java
public class TelaListaCarrosTablet ex tends LivroAndroidActivity {
@ 0verride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
// Liga a navegaao por Tabs
ActionBar actionBar = getActionBarQ ;
actionBar.setNavigationM ode(ActionBar.NAVIGATIO N_ M O DE_ TABS);
actionBar.setDisplay H omeAsllpEnabled(true);
String tipoParam = getIntent().getStringEx tra(Carro.TIPO );
// Carros - Clssicos
F ragmentListaCarros fragClassicos = new F ragmentListaCarrosQ ;
Bundle args = new Bundle();
args.putString(Carro.TIPO , Carro.TIP0_ CLASSIC0);
fragClassicos.setArguments(args);
// Carros - Luxo
F ragmentListaCarros fragLux o = new F ragmentListaCarros();
args = new Bundle();
args.putString(Carro.TIPO , Carro.TIP0_ LU X 0);
fragLux o.setArguments(args);
// Carros - Esportivos
F ragmentListaCarros fragEsportivos = new F ragmentListaCarros();
args = new BundleQ ;
args.putString(Carro.TIPO , Carro.TIP0_ ESP0RTIV0S);
fragEsportivos.setArguments(args);
// Tab 1 - Clssicos
Tab tabl = actionBar.newTab();
tabl.setTex t(R.string.menu_ classicos);
tabl.setTabListener(new TabSelecionaCarroListener(fragClassicos));
// Tab 2 - Luxo
Tab tab2 = actionBar.newTab();
tab2.setTex t(R.string.menu_ lux o);
tab2.setTabListener(new TabSelecionaCarroListener(fragLux o));
// Tab 3 - Esportivos
Tab tab3 = actionBar.newTabQ ;
tab3.setTex t(R.string.menu_ esportivos);
tab3.setTabListener(new TabSelecionaCarroListener(fragEsportivos));
Cap tulo 8 Mi ggi n do o apli cati vo para tablets
307
// Adiciona as Tabs
actionBar.addTab(tabl, Carro.TIP0_ CLASSIC0.eq uals(tipoParam));
actionBar.addTab(tab2, Carro.TIPO _ LU X O .eq uals(tipoParam));
actionBar.addTab(tab3, Carro.TIPO _ ESPO RTIVO S.eq uals(tipoParam));
}
gO verride
public boolean onO ptionsItemSelected(M enuItem item) {
switch (item.getltemldQ ) {
case android.R.id.home:
// Volta para o incio
Intent intent = new Intent(this, Main.class);
intent. addF lagS(Intent. F LAG_ ACTIVITY _ CLEAR_ TO P);
startActivity (intent);
return true;
}
return super.onO ptionsItemSelected(itera);
}
// Listener para trocar a lista de carros ao escolher uma Tab
private class TabSelecionaCarroListener implements ActionBar.TabListener {
private F ragment frag;
public TabSelecionaCarroListener(F ragment frag) {
this.frag = frag;
}
public void onTabSelected(Tab tab, android.app.F ragmentTransaction ft) {
if (frag != null) {
// Atualiz a o fragment com a lista de carros na esq uerda
// U tiliz a a compatibility library
F ragmentTransaction t = getSupportF ragmentM anagerQ .beginTransactionQ ;
t.replace(R.id.lay outEsq uerda, frag, null);
t.commit();
}
}
public void onTabU nselected(Tab tab, android.app.F ragmentTransaction ft) {
if (frag != null) {
// Remove utiliz ando a compatibility library
F ragmentTransaction t = getSupportF ragmentM anager().beginTransaction();
t.remove(frag);
t.commit();
}
}
public void onTabReselected(Tab tab, android.app.F ragmentTransaction ft) {
}
}
308
Google An droi d para T ablets
Primeiramente note que estamos recuperando a ActionBar no cdigo para ativar a
navegao por Tabs e tambm estamos ativando o boto H ome no lado esquerdo superior
para que, ao pressionar o cone da aplicao, seja feito um voltar para a tela de entrada.
ActionBar actionBar = getActionBarQ ;
actionBar. setNavigationH ode(ActionBar. NAVIGATI0N_ M 0DE_ T ABS);
actionBar.setDisplay H omeAsllpEnabled(true);
Como habilitamos o boto home na ActionBar, a ao android. R . id. home vai iniciar a
activity H ain novamente com o flag f l a g _ a c t i v i t y _ c l e a r _ t o p para derrubar todas as activities
da pilha e exibir novamente a H ain no topo. Em nosso simples projeto temos somente
duas telas, mas esse conceito de voltar a activity principal bem til no dia a dia.
A criao das Tabs tranqila. Para cada tipo clssico, luxo ou esportivo
criado um fragment com a classe F ragmentListaCarros, que criamos anteriormente.
Para utilizarmos sempre o mesmo fragment passamos o tipo do carro desejado
como parmetro pelo Bundle.
Por exemplo, a seguir podemos visualizar a criao do fragment para o tipo luxo.
F ragmentListaCarros fragLux o = new F ragmentListaCarrosQ ;
Bundle args = new Bundle();
args.putString(Carro.TIPO , Carro.TIP0_ LU X 0),
fragLux o.setArguments(args);
Feito isso, precisamos adicionar uma Tab, que ao ser selecionada vai abrir o
fragment que foi criado anteriormente. O cdigo a seguir, por exemplo, cria a Tab,
vinculando a ao para abrir o fragment que lista os carros de luxo.
Tab tabl = actionBar.newTabQ ;
tabl.setTex t(R.string.menu_ lux o);
tabl.setTabListener(new TabSelecionaCarroListener(fragLux o));
actionBar.addTab(tabl);
Ao clicar na Tab executado um listener, e para isso criamos a classe interna
TabSelecionaCarroListener. Como ela recebe o fragment no seu construtor, utilizado
um F ragmentTransaction para adicionar a tab dinamicamente no layout da esquerda.
public void onTabSelected(Tab tab, F ragmentTransaction ft) {
if (frag != null) {
// Atualiz a o frag com a lista de carros na esq uerda
// U tiliz a a compatibility library
F ragmentTransaction t = getSupportF ragmentH anager().beginTransaction();
t.replace(R.id.lay outEsq uerda, frag, null);
t.commit();
}
}
Cap tulo 8 Mi gran do o apli cati vo para tablets
309
Note que para trocar de Tab 'estamos utilizando o mtodo1'
" " gitSupportF ragmentH anagerO da biblioteca'de'compatibilidade para
obter a FragmentTransaction, porque nossos fragments esto utilizando
a biblioteca e no so nativos.
Depois de criar essa nova activity necessrio declar-la no And.roidManifest.xml.
< !-- Tablet -->
< activity android :name= " .activity .vll.TelaListaCarrosTablet" />
Feita a alterao, o arquivo deve estar assim:
ls An droi dMan i f est.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns: android= " http: //schemas. android. com/apk /res/android"
pack age= " br.livroandroid.carros"
android:versionCode= " l
android:versionName= " 1.0" >
< uses-sdk android:minSdk Version= " 4" android:targetSdk Version= " ll" />
< uses-permission android:name= " android.permission.INTERNET" />
< uses-permission android:name= android.permission.ACCE5S_ NETW 0RK _ STATE" />
< uses-permission android:name= " android.permission.W RITE_ EX TERNAL_ STO RAGE" />
opplication android:name= " br.livroandroid.carros.CarrosApplication
android:icon= " @ drawable/icon"
android:label= " @ string/app_ name"
android:theme= " gsty le/tema" >
< activity
android:label= " @ string/app_ name"
android:name='' .Main' >
<intent-filter >
< action android:name= " android.intent.action.M AIN" />
< category android:name= " android.intent.category .LAU NCH ER" />
</intent-filter>
< /activity >
<! -- smartphone -->
< activity android:name= " .activity .TelaListaCarros />
(activity android:name= " .activity .TelaDetalhesCarro" />
< activity android:name= " .activity .TelaSobre" />
< !-- tablet -->
< activity android:name= " .activity .vll.TelaListaCarrosTablet" />
< /application>
< /manifest>
310
Google An droi d para T ablets
Para utilizar essa nova activity turbinada com a navegao com Tabs vamos
precisar alterar o cdigo do dashboard, para abrir essa activity se a aplicao
estiver executando em um tablet, conforme demonstrado a seguir.
// Verifica se tablet com Android 3.x ou superior
boolean android3 = Androidlltils.isAndroid_ 3();
// Cria uma Intent com telas diferentes para smartphone e tablet
Intent intent = new Intent(contex t,android3 ? TelaListaCarrosTablet.class : TelaListaCarros.class);
A seguir podemos ver o cdigo atualizado da classe F ragmentDashboard.
Fragmen tDashboard.java
public class F ragmentDashboard ex tends LivroAndroidF ragment implements O nClick Listener {
private Button btEsportivos;
private Button btClassicos;
private Button btLuxo;
private Button btSobre;
public View onCreateView(android.view.Lay outInflater inflater, android.view.ViewGroup Container,
Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment_ dashboard, null);
btEsportivos = (Button) view.findViewById(R.id.btEsportivos);
btEsportivos. setO ndick Listener(this);
btClassicos = (Button) view.findViewByldfR.id.btClassicos);
btClassicos.setO nClick Listener(this);
btLux o = (Button) view.findViewByld(R .id.btLux o);
btLux o.setO nClick Listener(this);
btSobre = (Button) view.findViewByld(R.id.btSobre);
btSobre.setO nClick Listener(this);
return view;
}.
g verride
public void onClick (View v) {
Contex t contex t = getActivity Q ;
boolean redeO k = AndroidU tils.isNetwork Available(contex t);
if (redeOk ) {
// Verifica se tablet com Android 3.x ou superior
boolean android3 = AndroidU tils.isAndroid_ 3();
// Cria uma Intent com telas diferentes para smartphone e tablet
Intent intent = new
Intent(contex t,android3 ? TelaListaCarrosTablet.class : TelaListaCarros.class);
if (v = = btEsportivos) {
intent.putEx tra(Carro.TIPO, Carro.TIP0_ ESP0RTIV0S);
startActivity (intent);
Cap tulo 8 Mi gran do o apli cati vo para tablets
311
} else if (v == btClassicos) {
intent.putEx tra(Carro.TIPO, Carro.TIPO _ CLASSICO );
startActivity (intent);
} else if (v == btLux o) {
intent.putEx tra(Carro.TIPO , Carro.TIP0_ LU X 0);
startActivity (intent);
} else if (v == btSobre) {
startActivity (new Intent(contex t, TelaSobr .class));
}
} else {
Androidlltils.alertDialog(contex t,R.string.erro_ conex ao_ indisponivel);
}
}
}
Repare que a verso para smartphones com Android 4.X ICS
tambm vai utilizar essa activity com Tabs.
Finalmente podemos executar o projeto, e desta vez a verso para tablets possui
a navegao por Tabs na ActionBar, conforme a figura 8.11.
A wlifltc mi pcterat ctifgj J vtriVacorwtrsivtl e tmptgj omesmomotof V8d4.7litrose4M cvdomp. Oconjuntasuficiente
paralevar ocarro km/h. Algumasmtihorl* forim(?ius hmoGrintxia. Mm tmt0 ictuti, omotor (koucMtid*
maisconmlco, ndi queboJ p*tedoscompradoresnlo estejamultopreocuptdj comogastodcombustvel.
B
Figura 811 - Navegao com Tabs na ActionBar.
312
Google An droi d para T ablets
8.15 Criando os itens de menu na ActionBar
A prxima funcionalidade que vamos implementar na aplicao so os itens de
menu na ActionBar, e para isso vamos criar o seguinte arquivo de menu:
/res/men u-v11/men u_acti on bar.xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< menu x mlns:android= " http://schemas.android.com/apk /res/android">
<item
android:id= " @ + id/menu_ busca"
android:icon= " @ android:drawable/ic_ menu_ search"
android:title= " @ string/busca" :
android:showAsAction= " alway s"
android:orderInCategory = " 0"
/>
<item
android: id="(8+id/menu_ atualizar"
android:icon= " @ drawable/atualiz ar"
android:title= " @ string/atualiz ar"
android:showAsAction= " ifRoom"
/>
< item
android:id= " @ + id/menu_ sobre"
android:title= " @ string/sobre"
android:showAsAction= " never"
/>
< /menu>
Esse arquivo possui trs itens, que so o item de busca para filtrar os carros e
os botes para atualizar a lista de carros e visualizar a tela de sobre.
Note que estamos criando esse arquivo na pasta /res/menu-vll porque s vamos
utilizar ele na verso para tablets, e para que esse arquivo de menu funcione
necessrio que se criem as chaves (Bstring/ sobre e @string/ atualizar no arquivo /res/
values/strings.xml.
Tambm precisamos inserir a imagem atualizar.png na pasta /res/drawable-mdpi
do projeto. Como a imagem pequena e no precisa de muita definio, vamos
utilizar apenas a pasta de mdia densidade, at porque os tablets atuais, como o
Xoom, possuem densidade mdia. Mas, se necessrio, poderamos tambm inserir
uma imagem na pasta /res/drawable-hdpi.
Cap tulo 8 Mi gran do o apli cati vo para tablets 313
Voc pode encontrar a imagem atualizar.png no projeto que est disponvel no
link de download do livro.
A seguir podemos verificar o arquivo com todas as mensagens atualizadas que
vamos utilizar em nosso projeto. Aproveite e confira se o seu arquivo est igual.
f e /res/values/stri n gs.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
<resources>
string name= " app_ name" > Carros< /string>
< string name= " aguarde" > Por favor aguarde...< /string>
< string name= " copy right" > LivroAndroid - Todos os direitos reservados< /string>
< string name= " titulo>Carros Showcase< /string>
<!-- M ensagens -->
<string narae="erro_conexao_indisponivel">Conexo indisponvel, por favor ative sua rede mvel ou Wi-Fi.
< /string>
<string name=''erro_io''>Ooops, sistema indisponvel no momento.< /string>
<!-- Labels -->
<string name= " abrirSite" > Abrir o Site< /string>
<string name= " atualiz ar" > Atualiz ar< /string>
<string name= " busca" > Buscar< /string>
< string name= " menu_ lux o> Lux o</string>
< string name= " menu_ esportivos" > Esportivos< /string>
< string name= ' 'menu_ classicos" >Clssicos</string>
<string name= " menu_ sobre" > Sobre< /string>
< string name= " nome>Noiiie</string>
< string name= " descricao" > Descrio< /string>
< string name=''sobre> Sobre</string>
</resources>
Depois de criar corretamente o arquivo com o menu da aplicao vamos alterar
o fragment que exibe a lista de carros para utilizar esse arquivo. Mas como vamos
ter que utilizar futuramente a classe SearchView e ela no pode ser utilizada em
verses anteriores do Android, vamos criar outro fragment para a lista de carros,
que ser utilizado somente nas verses para Android 3.x ou superior.
A classe F ragmentListaCarrosTablet ser filha de F ragmentListaCarros e pode ser visu
alizada a seguir.
)
314 s Google An droi d para T ablets
i S) Fragmen tLi staCarrosT ablet.java
public class F ragmentListaCarrosTablet ex tends F ragmentListaCarros {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
// Informa q ue este F ragment desej a adicionar itens de menu na ActionBar
setH asO ptionsM enu(true);
}
@ 0verride
public void onCreateO ptionsM enu(M enu menu, Menulnflater inflater) {
inflater. inflate(R.iaenu.menu_actionbar, menu);
}
}
Note que um fragment pode inflar um menu normalmente, mas para isso ele
precisa chamar o mtodo setH asO ptionsM enu(true) para indicar que vai adicionar itens
de menu na ActionBar.
Depois de criar esse fragment customizado precisamos adicion-lo ao layout da
verso para tablets no lugar do fragment antigo. Para isso necessrio que se atu
alizem o cdigo da activity TelaListaCarrosTablet que controla as Tabs na ActionBar.
Portanto, faa a seguinte alterao nessa classe, instanciando o tipo correto do
fragment que exibe a lista de carros:
// Carros - Clssicos
F ragmentListaCarros fragClassicos = new F ragmentListaCarrosTabletQ ;
Bundle args = new BundleQ ;
args.putString(Carro.TIPO , Carro.TIP0_ CLASSIC0);
fragClassicos.setArgunents(args);
// Carros - Luxo
F ragmentListaCarros fragLuxo = new F ragmentListaCarrosTabletQ ;
args = new Bundle();
args.putString(Carro.TIPO , Carro.TIP0_ LU X 0);
fragLux o.setArguments(args);
// Carros - Esportivos
F ragmentListaCarros fragEsportivos = new F ragmentListaCarrosTabletQ ;
args = new BundleQ ;
args.putString(Carro.TIPO, Carro,TIP0_ ESP0RTIV0S);
fragEsportivos.setArguments(args);
Depois de adicionar os itens de menu na ActionBar, o resultado deve ser o que
se observa na figura 8.12.
Cap tulo 8 Mi gran do o apli cati vo para tablets
315
Cadillac Eldorado
Owrpwl Odi bc ESdeiadsf
OTucl er foi realmente umalncvaSo no murdo do deuj n automvel, e embora o modelo WS ot o nico modelo Jproduzido sei
efeito sobre o mundo dos automveis ainda pede ser sentidJat hoje. Preaon Tucfcef e Al ei Tremulis projetou o Todcer como uma
tentativa de entrar na Indstria automotiva, e apesar de apenas um punhada de carros foram produzidos os recursos que estavam
presentes naqueles carros eram extremamente inovador para a poca.
Figura 8.12 -ActionBar com itens de menu.
8.16 Implementando a ao do item atualizar na ActionBar
O prximo passo implementar as aes que sero executadas pelos itens de
menu da ActionBar, e neste momento vamos implementar as aes que fazem a
atualizao da lista de carros e tambm a que exibe o fragment de sobre.
Para isso vamos atualizar a classe F ragmentListaCarrosTablet que acabamos de criar
para tratar essas aes conforme demonstrado a seguir.
i i Fragmen tLi staCarrosT ablet.java
public class F ragmentListaCarrosTablet ex tends F ragmentListaCarros {
private M enu menu;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
// Informa que este Fragment desej a adicionar itens de menu na ActionBar
setH asO ptionsM enu(true);
}
316
Google An droi d para T ablets
gO verride
public void onCreateO ptionsM enu(M enu menu, Menulnflater inflater) {
this.menu = menu;
inflater.inflate(R.menu.menu_ actionbar, menu);
}
(SOverride
public boolean onO ptionsItemSelected(M enuItem item) {
switch (item.getltemldQ ) {
case R.id.menu_ atualiz ar:
atualiz arViewM enuAtualiz ar(true);
startTransacao(this);
return true;
case R.id.menu_ sobre:
View lay outDireita = getActivity ().findViewBy Id(R.id.lay outDireita);
if (lay outDireita != null) {
F ragmentTransaction ft = getF ragmentM anager().beginTransaction();
F ragmentSobre sobre = new F ragmentSobreQ ;
ft.replace(R.id.lay outDireita, sobre);
ft.commit();
} else {
startActivity (new Intent(getActivity (), TelaSobre.class));
}
return true;
}
return super.onO ptionsItemSelected(item);
}
public void atualiz arViewM enuAtualiz arfboolean atualiz ar) {
if (menu != null) {
final H enultem item = menu.findItem(R.id.menu_ atualizar);
if (item != null) {
if (atualiz ar) {
item.setActionView(R.lay out.actionbar_ progress);
} else {
item.setActionView(null);
}
}
}
}
(SOverride
public void atualiz arViewQ {
super. atualiz arViewQ ;
atualiz arViewM enuAtualiz ar(false);
}
}
Repare que para atualizar a lista de carros o mtodo startTransacao(this) est
sendo chamado.
gO verride
public boolean onO ptionsItemSelected(H enuItem item) {
switch (item.getltemldO ) {
case R.id.menu_ atualiz ar:
atualiz arViewM enuAtualiz ar(true);
startTransacao(this);
return true;
Cap tulo 8 Mi gran do o apli cati vo para tablets 317
}
. Mas para melhorar ainda mais a aplicao vamos caprichar um pouco no
visual e inserir uma animao no item de menu atualizar, conforme a figura 8.13.
Figura 8.13 - Animao com ProgressBar na ActionBar.
Para isso estamos utilizando o mtodo atualiz arViewM enuAtualiz ar(true), o qual vai
substituir a imagem padro do item de menu atualizar por um ProgressBar que
vai exibir uma animao temporria na barra enquanto a atualizao realizada.
Esse arquivo pode ser visualizado a seguir.
i
f i a / r e s / l a y o u t - v l 1/acti on bar_progress.xml
< F rameLay out x mlns:android= " http://schemas.android.com/apk /res/android
android:lay out_ width= " 64dp
android:lay out_ height= " wrap_ content"
android:gravity = center">
< ProgressBar
android:lay out_ width= " 32dp"
android:lay out_ height= 32dp"
android:lay out_ gravity = " center"
sty le= " ? android:attr/indeterminateProgressSty le" />
< /F rameLay out>
Note que a implementao do mtodo atualiz arViewM enuAtualiz ar(true) simples, e
basicamente o objeto H enultem correspondente ao atualizar recuperado, e chamado
o mtodo setActionView(lay out) para atualizar a view que esse item de menu exibe.
final M enultem item = nenu.findItem(R.id.menu_ atualiz ar);
item.setActionView(R.lay out.actionbar_ progress);
Feito isso, j podemos executar a aplicao, pois o item de menu atualizar na
ActionBar j estar funcionando.
Note que o item sobre tambm est inserindo o F ragmentSobre no layout utili
zando uma F ragmentTransaction da seguinte forma:
case R.id.menu_ sobre:
View lay outDireita = getActivity Q .findViewBy Id(R.id.lay outDireita);
if (lay outDireita != null) {
F ragmentTransaction ft = getF ragmentH anagerQ .beginTransactionQ ;
F ragmentSobre sobre = new F ragmentSobreQ ;
ft.replace(R.id.lay outDireita, sobre);
ft.commitQ ;
} else {
startActivity (new Intent(getActivity (), TelaSobre.class));
}
J implementamos as aes de dois itens de menu, mas ainda est faltando o
mais legal deles, que o item de busca, que vai utilizar um Searchview para filtrar
a lista de carros.
318 Google An droi d para T ablets
Cap tulo 8 Mi gran do o apli cati vo para tablets
319
8.17 I mplementado a busca de carros com o SearchView
Um recurso interessante que vamos adicionar no projeto agora um item de
menu para filtrar os carros da lista, e para isso vamos utilizar o SearchView que
estudamos no captulo 5.
Apenas para refrescar a memria, a seguir podemos visualizar o arquivo /res
menu-vll/menu_actionbar.xml. Note que o primeiro item de menu o de busca, o
qual vamos utilizar para inserir um SearchView como a view a ser utilizada para
renderizar esse item de menu.
i s) /res/men u-v11/men u_acti on bar.xml
< ? x ml version=',1.0'' encoding= " utf-8" ? >
<menu x mlns:android= " http://schemas.android.com/apk /res/android>
citem
android:id= "(3+id/menu_busca"
android:icon= " @ android:drawable/ic_ menu_ search"
android:title= " @ string/busca"
android:showAsAction= " alway s"
android:orderInCategory = " 0"
/>
< item
android:id= " @ + id/menu_ atualiz ar"
android:icon= " @ drawable/atualiz ar"
android:title= " @ string/atualiz ar"
android:showAsAction= ifRoom"
/>
< item
android:id= " @ + id/menu_ sobre
android: title='' @string/sobre"
android:showAsAction= never"
/>
< /menu>
A primeira coisa que vamos fazer para implementar essa funcionalidade
substituir o item de menu busca pelo componente SearchView, e isso feito da
seguinte forma:
gO verride
public void onCreateO ptionsM enu(M enu menu, Menulnflater inflater) {
this.menu = menu;
inflater.inflate(R.menu.menu_actionbar, menu)j
M enultem item = menu.findItem(R.id.menu_busca);
SearchView sv = new SearchView(getActivity O );
320
Google An droi d para T ablets
sv.setO nQ uery Tex tListener(new F iltroListener());
item.setActionView(sv);
}
*
Para filtrar os carros basta implementarmos a interface android.widget.SearchView.
O nQ uery Tex tListener como demonstrado a seguir.
II Listener para filtrar os carros
private class F iltroListener implements O nQ uery Tex tListener {
gO verride
public boolean onQ uery Tex tChange(String tex toParcial) {
Log.i(" livroandroid," onQ uery Tex tChange: " + tex toParcial);
if (" .eq uals(tex toParcial)) {
// Se vazio, volta a lista original
atualiz arViewQ ;
}
return false;
}
gO verride
public boolean onQ uery Tex tSubmit(String tex toF inal) {
Log.i(" livroandroid" ," onQ uery Tex tSubmit: " + tex toF inal);
if (carros != null) {
List< Carro> list = new Array List< Carro> ();
for (Carro carro : carros) {
boolean contains = carro.nome.toU pperCase().contains(tex toF inal.toU pperCase());
if (contains) {
list.add(carro);
}
}
// Ex ibe no Listview um Adapter com apenas a lista q ue fez o filtro
listview.setAdapter(new CarroAdapter(getActivity (), list));
AndroidU tils.closeVirtualK ey board(getActivity (), listview);
}
return false;
}
}
A implementao des.se filtro simples. Basicamente, a lista de carros original
percorrida e efetuada uma comparao em memria com os nomes de todos
os carros. Aqueles que se enquadram nos critrios da busca so adicionados em
uma lista temporria. Essa lista, por sua vez, passada ao Adapter do Listview, para
que seja recriada com a nova lista de carros.
Cap tulo 8 Mi gran do o apli cati vo para tablets 321
Depois que a busca efetuada, note que estamos executando o mtodo
AndroidU tils.closeVirtualK ey board(activity , listView) para que o teclado virtual que foi
utilizado para digitar o texto seja fechado automaticamente. Esse mtodo ainda
no existe em nossa classe utilitria Androidutils, ento vamos adicion-lo l.
// F echa o teclado virtual, se aberto (view com foque)
public static boolean closeVirtualK ey board(Contex t context, View view) {
// F echa o teclado virtual
InputH ethodM anager imm
= (InputM ethodM anager)contex t.getSy stemService(Contex t.INPU T_ M ETH O D_ SERVICE);
if (imm != null) {
boolean b = imm.hideSoftInputF romW indow(view.getW indowTok en(), 6);
return b;
}
return false;
}
Lembre-se: a classe Androidutils foi criada n o captulo 2 e se encontra
n o proj eto d e biblioteca LivroAndroid-AndroidU tls.
Feita essa alterao, ao selecionar o item de menu busca o Searchview vai expandir
e exibir um campo de texto para o usurio digitar o nome do carro desejado para
que seja feita a busca, conforme podemos visualizar na figura 8.14.
J StVfeB TudceMM! .
Chevroet Carvett .
.............
ChrvreJ tt ImpiLa Coupe .
C B k tiffle Cenvtftibk .
Oetrfe
Chevrolet W-Ar .
OTiKktr toi reaWiente uma inovato no mundo do esign automvel. e embora o modelo 1948!wo nico modelo l produzido seu
efeito tobre o mundo dos automveis ainda pode ier sentida athoje. Presion Tuckrr e Alei Trtmulis pro|eiou o Tudcer como uma
tentativa de entrar na indana automotiva, e apesar de apenas umpunhado de carros foramprodundos os recursos que estavam
presentes naqueles carros er*meitremamente inmador para i poca.
Cadlllat EMerad .
Ferrari 250 it o .
^ f l p V r C*faiK*
^ D o d f c Challcnjer .
Onc(k)
Camaressm .
Onrrifto
, Ford Muitanr H76 .
Oncriffa
r n i
Figura 814 - Filtro de carros com o SearchView.
322
Google An droi d para T ablets
Neste momento podemos digitar algum texto, como, por exemplo, Che , e
o filtro ser efetuado na lista de carros. A figura 8.15 demonstra o resultado do
filtro. Como o esperado, somente os carros que possuem o texto Che no nome
foram selecionados.
OOi evrolel corvctte t umMm um dai co jrrtigo. encnurado nos l i es i irtomobitiuicos Interruaorui s. ele foi o pri meiro carro
produoto inteiramente dot Estsdoi Urudov a modelo de 19SS. pertencente i p<i mHi agr^Jo do corvt neum tfotmisconfteudo*
e era uma pot nda ru cpot j em que (M atado. carro di dco di egr/ a a 170 km/ h e era bem leve. o que esuva te [ornando
t endi noa qwcdoel e comeou a ser faeriudo em 1553.
Figura 815 - Filtro de carros efetuado com o SearchView.
O cdigo completo e atualizado da classe F ragmentListaCarrosTablets pode ser
visualizado a seguir.
S i Fragmen tLi staCarrosT ablet.java
public class F ragmentListaCarrosTablet ex tends F ragmentListaCarros {
private M enu menu;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
// Informa q ue este F ragment desej a adicionar itens de menu na ActionBar
setH asO ptionsM enu(true);
}
gO verride
public void onCreateO ptionsM enu(M enu menu, Menulnflater inflater) {
this.menu = menu;
inflater.inflate(R.menu.menu_actionbar, menu);
M enultem item = menu.findItem(R.id,menu_ busca);
Cap tulo 8 Mi gran do o apli cati vo para tablets
C
SearchView sv = new SearchView(getActivity Q );
sv.setO nQ uery Tex tListener(new F iltroListener());
item.setActionView(sv);
}
gO verride
public boolean onO ptionsItemSelected(M enuItem item) {
switch (item.getltemld()) {
case Rrid.menu_ atualiz ar:
atualiz arViewH enuAtualiz ar(true);
startTransacao(this);
return true;
case R.id.menu_ sobre:
View lay outDireita = getActivity ().findViewById(R.id.lay outDireita);
if (lay outDireita != null) {
F ragmentTransaction ft = getF ragmentM anager().beginTransaction();
F ragmentSobre sobre = new F ragmentSobreQ ;
ft.replace(R.id.lay outDireita, sobre);
ft.commit();
} else {
startActivity (new Intent(getActivity(), TelaSobre.class));
}
return true;
}
return super.onO ptionsItemSelected(item);
}
public void atualiz arViewH enuAtualiz ar(boolean atualiz ar) {
if (menu != null) {
final M enultem item = menu.findItem(R.id.menu_ atualiz ar);
if (item != null) {
if (atualiz ar) {
item.setActionView(R.lay out.actionbar_ progress);
} else {
item.setActionView(null);
}
}
}
}
gO verride
public void atualiz arViewQ {
super.atualiz arView();
atualiz arViewH enuAtualiz ar(false);
}
// Listener para filtrar os carros
private class F iltroListener implements O nQ uery Tex tListener {
324
Google An droi d para T ablets
g verride
public boolean onQ uery Tex tChange(String tex toParcial) {
Log.i(" livroandroid" , " onQ uery Tex tChange: + tex toParcial);
if C" .eq uals(tex toParcial)) {
// Se vazio, volta a lista original
atualiz arView();
}
return false;
}
g verride
public boolean onQ uery Tex tSubmit(String tex toF inal) {
Log.i(" livroandroid" , " onQ uery Tex tSubmit: " + textoF inal);
if (carros != null) {
List< Carro> list = new Array List< Carro> ();
for (Carro carro : carros) {
boolean contains = carro.nome.tol/pperCase().contains(tex toF inal.tollpperCase());
if (contains) {
list.add(carro);
}
}
// Ex ibe no Listview um Adapter com apenas a lista q ue fez o filtro
listview.setAdapter(new CarroAdapter(getActivity (), list));
Androidlltils.closeVirtualK ey board(getActivity (), listview);
}
return false;
}
}
}
Chegamos ao final deste captulo, e agora a aplicao para tablets est funcio
nando e usufruindo das novas APIs de Fragments e ActionBar. De quebra, reu
tilizamos os fragments na verso para smartphone para evitar cdigo duplicado.
L embre-se que fizemos um refactor na verso smartphone para utilizar frag
ments porque o projeto era pequeno, mas em casos reais isso sempre ter que
ser analisado com calma.
Ao utilizar os fragments podemos criar componentes reutilizveis para separar
as responsabilidades e simplificar o cdigo da activity. Se voc decidir utilizar os
fragments em seu projeto, estar tomando uma tima deciso.
No prximo captulo vamos verificar como a aplicao executa no Android
4.x ICS.
CAPITULO 9
Android 4.x - Ice Cream
Sandwich
O Android 4.0, conhecido como Ice Cream Sandwich (ICS), foi uma das mais
aguardadas verses do Android, pois tem o objetivo de unificar a plataforma de
desenvolvimento entre os tablets e smartphones.
Com o ICS as novas APIs, como Fragments e ActionBar, podem ser utilizadas
de forma nativa mesmo em smartphones.
Neste captulo vamos continuar o projeto dos carros e executar a verso que
fizemos nos tablets 3.x Honeycomb em um smartphone com Android 4.x ICS.
9.1 Introduo
O lanamento do Android 4.0 conhecido como Ice Cream Sandwich (ICS), foi
uma das mais aguardadas verses do Android, pois tem o objetivo de unificar a
plataforma de desenvolvimento entre os tablets e smartphones.
Com o ICS as novas APIs como Fragments e ActionBar podem ser utilizadas
de forma nativa, mesmo em smartphones.
Neste captulo vamos continuar o projeto dos carros e executar a verso que
fizemos nos tablets 3.x Honeycomb em um smartphone com Android 4.x ICS.
9.2 Configurando o emulador do Android 4.x
Para a instalao da plataforma do Android 4.x ICS basta abrir o assistente de
instalao do SDK e baixar a verso do Android 4.x ou superior.
\
325
-*
326 Google An droi d para T ablets
A figura 9.1 demonstra a instalao da plataforma do A ndroid 4.0 ICS, o qual
possui API Levei =14.
J Android SOK Manager
S p l Toob_
_____ X An*oid SC*c_Toob
> * - | Ar&od SDK Ptatfornrtoab
jp\si pHg
H ;&Irat af c<J _
- . . . . . ]
_L
I__0 J ^ Doamentstjan/or Android SDK __
S *** SDtPbtfoco
_j-0<S Stmpies/arSCX
- - 0 S<AHME4Sv7*Srsteaimage
^ 0 %. eeogkAfibbrSoogkne.__
\1 to d rti d 3.2(AF113)
ffl- J Aood3.1(Af112)
__14 J _________i ^ NotratBad^
14 ________ ^ fn xrstied
i /J otnstoSed _
^ N o f r e t i e d _
fJ ot rgtied
a i )< r t Q3 . 0 (A n M )
' ] a rt f rod 2.3.3 (API 10)
a -D C S reJrad 2,2 (API 8)
B a ] if iM 2. (API 7)
Figura 9.1 - Baixando o Android 4.x.
Lembre-se de sempre manter atualizado o Android SDK Tools e o
Android SDK Platform-tools.
O Android 4.0 ICS possui API Levei =14.
Depois de instalado o Android 4.x, podemos criar um emulador da maneira
tradicional, conforme demonstrado na figura 9.2.
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch
327
Figura 9.2 - Criando um emulador para Android 4.x.
9.3 Android 4.0 ICS possui API Levei = 14
Devido ao Android 4.0 ICS possuir a API Levei = 14, ele ir compartilhar da mesma
verso para tablets que desenvolvemos no captulo anterior.
Na tela de dashboard abrimos uma verso customizada para tablets da se
guinte maneira:
// Verifica se tablet com Android 3.x ou superior
boolean android3 = AndroidU tils.isAndroid_ 3{ );
// Cria uma Intent com telas diferentes para smartphone e tablet
Intent intent = new Intent(context,android3 ? TelaListaCarrosTablet.class : iplaListaCarros.class);
328
Google An droi d para T ablets
Lembre-se que o mtodo AndroidU tils.isAndroid_ 3() compara se a API Levei do
aparelho 11 ou superior, por isso a activity customizada com Tabs tambm ser
utilizada no Android 4.x.
Mas como os layouts que dividem a tela em duas partes foram criados na pasta
/res/layout-large-land-vll com o prefixo large, eles no sero utilizados na verso
para smartphones.
9.4 Executando a aplicao no Android 4.x ICS
Depois de criar o emulador com o A ndroid 4.x podemos executar o projeto atu
al dos carros, e para alegria geral dos desenvolvedores Android, a aplicao vai
executar normalmente.
A figura 93 exibe a tela do dashboard executando no emulador. Podemos
verificar nessa figura que a barra com o cabealho utilizada na verso para smar
tphones aparece logo abaixo da ActionBar.
Figura 9.3 - Dashboard dos carros em um smartphone com Android 4.x.
Isso acontece porque o arquivo de layout /res/layout/main.xml da verso smar
tphone declara o cabealho logo no incio da seguinte forma:
< include lay out= " @ lay out/include_ header" />
A seguir podemos visualizar e relembrar o cdigo-fonte completo desse arquivo.
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch 329
a /res/layout/mai n .xml
< ? xml version= " 1.0" encoding= utf-8" ? > ^
< LinearLay out x mlns:android=" http:' //schemas.android.com/apk /res/android"
android:layout_width=" fill_parent"
android:layout_ height= " fill_ parent"
android:back ground= " @ color/fundo"
android:gravity = center"
android:orientation= " vertical" >
< indude lay out= " @ lay out/include_ header" />
<fragment
android:id= @ + id/fragDashboard"
android:lay out_ width= " match_ parent"
android:lay out_ height= " 0dp"
android:lay out_ weight= " l
dass= " br.livroandroid.carros.fragments.F ragmentDashboard" >
</fragment>
< indude lay out= " @ lay out/indude_ footer" />
< /LinearLayout>
No queremos exibir esse layout de cabealho na verso para Android 4.x
ICS, pois vamos utilizar a ActionBar para isso. Ento uma soluo seria criar um
arquivo /res/layout-vll/main.xml ou /res/layout-vWmain.xml customizado para API
Levei =II ou superior e remover esse include.
Mas vamos analisar outras maneiras. Durante o captulo 3 demonstramos
algumas tcnicas utilizadas para exibir ou no o cabealho no layout da tela, que
criado pelo arquivo lresllayoutHnd.ude_header.xml.
Uma das tcnicas era chamar o mtodo setVisibility (View.GO NE) da classe View,
para esconder o cabealho em tempo de execuo. Para isso podemos utilizar um
cdigo parecido com este:
boolean tablet3x = Androidutils.isAndroid_ 3_ Tablet(this);
if (tablet3x) {
LinearLay out lay outH eader = (LinearLay out) findViewByld(R.id.lay outHeader)j
if (lay outH eader != null) {
lay outH eader. setVisibility (View. GO NE);
}
}
Outra soluo seria criar um arquivo /res/layout-vU/include_header.xml vazio, para
que no momento que este for adicionado na tela, nenhum espao seja ocupado.
330
Google An droi d para T ablets
i i /re s / l a y o u t- v l 1/i n dude_header.xml
<?xnil version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:id= " @ + id/lay outH eader"
android:lay out_ width= wrap_ content"
android:lay out_ height= " edp"
>
< ! -- Vaz io -->
< /LinearLay out>
Para continuar nossa aplicao vou escolher essa ltima opo e criar o arquivo
/res/layout-vll/include_header.xml vazio, que ser utilizado por aparelhos com API
Levei = 11 ou superior.
Note que no tivemos esses problemas na verso para tablets porque
customizamos os arquivos de layout e no inclumos esse cabealho.
Ao final dessa alterao podemos executar o projeto, e o resultado dever ser
igual figura 9.4, agora sem a barra azul de cabealho no layout da tela inicial.
Nesse momento somente a ActionBar fica visvel.
Clssicos Luxo
Esportivos Sobre
Figura 9.4 - Dashboard dos carros em um smartphone com Android 4.x somente com
ActionBar.
9.5 Verificando as outras telas da aplicao no Android 4.x ICS
Agora vamos verificar as demais telas da aplicao executando no ICS, e para nossa
alegria a lista de carros com a navegao por Tabs na ActionBar est executando
normalmente, conforme a figura 9.5.
Tambm podemos executar essa tela na horizontal. Nesse caso a ActionBar
automaticamente renderiza um layout parecido com a verso para tablets, con
forme a figura 9.6.
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch ^ 331
LUXO ESPORTIVOS
Chevrolet Corvette
1Chevrolet (mpa/a Coupe
1Cadillac DevilleConvertihle
Chevrolet Bel-AJ r
Figura 9.5 - Lista de carros no ICS.
Tuckerl948
O Tucker foi realmente uma inovao no mundo do design automvel, e ~
Chevrolet Corvette ^
O Chevrolet corvette tambm um dssico antigo, encontrado nossal~.
Chevrolet Impala Coupe y
O Impala foi lanado em 1958, como a configurao de acabamento To._
Cadillac Deville Convertible y
0 Cadillac 1968 conversvel terminado em suas desejveis cores origi-
Chevrolet Bel-Air
Figura 9.6 - Lista de carros no ICS na horizontal.
332
Google An droi d para T ablets
Na verso para smartphones no dividimos a tela em duas partes como nos
tablets, ento naturalmente a tela de detalhes do carro exibida em outra activity,
conforme a figura 9.7.
a
0 Tucker foi realmente uma inovao no mundo
do design automvel e embora o modelo 1948
foi o nico modelo J produzido seu efeito sobre
o mundo dos automveis ainda pode ser sentida
at hoje. Preston Tucker e Alex Tremufis projetou
o Tucker como uma tentativa de entrar na
indstria automotiva, e apesar de apenas um
punhado de carros foram produzidos os
recursos que estavam presentes naqueles
carTos eram extremamente inovador para a
poca.
mtodroid-Tdossdrntosrcstvados '
Figura 9.7 - Tela de detalhes no ICS.
9.6 Customizando o texto da ActionBar
Repare que, at o momento, na tela de detalhes o nome do carro no est sendo
exibido, e o texto da ActionBar ficou com o valor padro, que o nome da aplicao.
Isso acontece porque na verso para smartphone estvamos exibindo o nome
do carro na barra azul do arquivo de cabealho que acabamos de remover na
verso para o ICS.
A classe F ragmentDetalhesCarro exibe o nome do carro no cabealho com o seguinte
cdigo:
Tex tView tH eader = (TextView) getActivity().findViewById(R.id.tH eader);
if (tH eader != null) {
tHeader-. setTex t (carro, nome);
}
Como o arquivo de cabealho foi substitudo pela ActionBar na verso para
ICS, vamos exibir o nome do carro selecionado direto no ttulo da ActionBar. Para
isso vamos criar uma classe utilitria conforme demonstrado a seguir.
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch 333
ti Acti on BarUti l.java
public class ActionBarU til {
public static void setBack groundImage(Activity activity) {
if (Androidutils.isAndroid_ 3()) {
ActionBar actionBar = activity .getActionBar();
if (actionBar != null) {
Resources res = activity .getResourcesQ ;
actionBar. setBack groundDrawable(res.getDrawable(R.drawable.shape_ header));
}
}
}
public static void setActionBarex t(Activity activity ,String s) {
if (Androidutils.isAndroid_ 3Q ) {
ActionBar actionBar = activity .getActionBar();
if (actionBar != null) {
actionBar.setTitle(s);
}
}
}
}
Depois que essa classe estiver criada, altere o cdigo da classe F ragmentDetalhes
Carro para exibir o nome do carro selecionado da seguinte forma:
Tex tView tH eader = (Tex tView) getActivity().findViewById(R.id.tH eader);
if (tH eader != null) {
tH eader.setTex t(carro.nome);
} else {
if ( ! Androidutils.isTablet(getActivity ())) {
// Somente atualiz a se no for um tablet
ActionBarU til.setActionBarTex t(getActivity Q , carro.nome);
}
}
Depois de feita essa alterao, a tela de detalhes vai exibir o nome do carro na
ActionBar, conforme a figura 9.8.
334 Google An droi d para T ablets
O Tucker foi realmente uma inovao no mundo
do design automvel, e embora o modelo 1948
foi o Onico modelo j produzido seu efeito sobre
o mundo dos automveis ainda pode ser sentida
at hoje. Preston Tucker e Alex Tremulis projetou
o Tucker como uma tentativa de entrar na
indstria automotiva, e apesar de apenas um
punhado de carros foram produzidos os
recursos que estavam presentes naqueles
carros eram extremamente inovador para a
poca.
Figura 9.8 - Tela de detalhes no ICS com o nome do carro na ActionBar.
9.7 Utilizando o menu fsico para acionar os itens de menu da ActionBar
L embre-se que na verso tablet existia um item de menu na ActionBar, chamado
sobre, que exibia a tela de sobre o livro.
Esse item de menu ficava escondido na ActionBar at ser acionado pelo usu
rio. Mas em smartphones, por questes de espao disponvel na tela, esse item
de menu fica junto com o tradicional menu de opes, que acionado pelo menu
fsico presente nos smartphones.
Para visualizar o item de menu sobre basta pressionar o menu fsico, conforme
demonstrado na figura 9.9.
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch
335
Leblanc Mirabeau >
4 1 Sheiby Supereare
XNN'>> Ultimate
Figura 9.9 - Item de menu sobre sendo pressionado.
Ao selecionar a opo sobre no menu a activity TelaSobre ser iniciada, conforme
podemos visualizar na figura 9.10.
flfTDROI D
Est e l i vro dedi cado aos
desenvol vedores Androi d que desej am.
apri morar seus conheci ment os e
est udar as novas f unci onal i dades
di sponvei s no Androi d 3.x, como
f ragment s e act i onbar.
Q
UvroAndroid Todos os direitos reservados
Figura 910 - Tela de sobre.
336
Google An droi d para T ablets
9.8 Troca de orientao na verso smartphone com Android 4.x ICS
O projeto dos carros j est executando sem problemas em smartphones com An
droid 4.x ICS, mas experimente girar a tela na listagem de carros e ver o que acontece.
Por exemplo, selecione a Tab Luxo e deixe o aparelho na vertical. Depois dis
so gire a tela para a horizontal e veja que os carros so recarregados da internet
novamente, exigindo uma nova conexo de dados para trfego das informaes,
o que no nada bom.
Note que no percebemos esse problema durante o desenvolvimento da
verso para tablets, pois focamos no layout apenas na horizontal, mas o mesmo
problema j acontecia.
Para solucionar esse problema precisamos utilizar os mtodos do ciclo de vida,
como O onSavelnstanceState(bundle) ou o onConfigurationChanged(config). Portanto, vamos
implementar esses detalhes agora.
Para salvar o estado da ActionBar vamos salvar o ndice da Tab selecionada no
mtodo onSavelnstanceState(bundle) conforme demonstrado a seguir.
(SOverride
protected void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
int tabldx = getActionBar().getSelectedNavigationIndex ();
outState.putlnt(" tabldx " , tabldx);
}
Depois, durante a criao da nova activity, vamos recuperar o ndice da ltima
Tab e deix-la novamente selecionada da seguinte forma:
if (savedlnstanceState != null) {
// Restaura o estado
int tabldx = savedlnstanceState.getlntCtabldx ' ' );
atualiz arTabs = false;
actionBar.setSelectedNavigationltem(tabldx );
}
Mas, conforme explicamos no captulo 6, a F ragmentTransaction persiste durante
o ciclo de vida da activity. Ento, ao girar a tela o fragment selecionado na Tab j
estar adicionado no layout.
O problema que precisamos manter o estado da Tab selecionada, mas sem
pre que o ndice selecionado da Tab alterado, o listener da ActionBar invocado
para trocar de Tab, e nesse momento alteramos o fragment para a lista de carros
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch 337
que est sendo exibida. Se isso for feito na primeira vez que a troca de orientao
ocorre, o fragment atual ser substitudo, e a tela perder o estado.
Portanto, vamos utilizar m flag chamado " atualiz arTabs" , que somente da primei
ra vez ficar false, para que l no listener o fragment no seja adicionado novamente
na primeira vez que a activity executade porque a F ragmentTransacto foi persistida.
O cdigo-fonte atualizado da classe TelaListaCarrosTablet pode ser visualizado a
segir, e salva corretamente o estado da Tab selecionada ao trocar a orientao da tela.
(i T elaLi staCarrosT ablet.java
public class TelaListaCarrosTablet ex tends LivroAndroidActivity {
// F lag para no atualiz ar o F ragmentTransaction das Tabs ao girar
private boolean atualiz arTabs;
gO verride
protected void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
// Liga a navegaao por Tabs
ActionBar actionBar = getActionBarQ ;
actionBar.setNavigationM ode(ActionBar.NAVIGATIO N_ M O DE_ TABS);
actionBar.setDisplay H omeAsllpEnabled(true);
// Carros - Clssicos
F ragmentListaCarros fragClassicos = new F ragmentListaCarrosTabletQ ;
Bundle args = new BundleQ ;
args.putString(Carro.TIPO , Carro.TIP0_ CLASSIC0);
fragClassicos.setArguments(args);
// Carros - Lux o ,
F ragmentListaCarros fragLux o = new F ragmentListaCarrosTabletQ ;
args = new BundleQ ;
args.putString(Carro.TIPO, Carro.TIPO J .U X O );
fragLux o.setArguments(args);
// Carros - Esportivos
F ragmentListaCarros fragEsportivos = new F ragmentListaCarrosTabletQ ;
args = new BundleQ ;
args.putString(Carro.TIPO , Carro.TIP0_ ESP0RTIV0S);
fragEsportivos.setArguments(args);
// Tab 1 - Clssicos
Tab tabl = actionBar.newTabQ ;
tabl.setTex t(R.string.menu_ classcos);
tabl.setTabListener(new TabSelecionaCarroListener(fragClassicos));
// Tab 2 - Luxo
Tab tab2 = actionBar.newTabQ ;
338
Google An droi d para T ablets
tab2.setTex t(R.string.menu_ lux o);
tab2.setTabListener(new TabSelecionaCarroListener(fragLux o));
// Tab 3 - Esportivos
Tab tab3 = actionBar.newTab();
tab3.setTex t(R.string.menu_ esportivos);
tab3.setTabListener(new TabSelecionaCarroListener(fragEsportivos));
atualiz arTabs = true;
// Adiciona as Tabs (verifica q ual deve ser selecionada pelo flag no segundo parmetro)
String tipoParam = getlntent().getStringEx tra(Carro.TIPO );
actionBar.addTab(tabl, savedlnstanceState = null ? Carro.TIPO_CLASSICO.equals(tipoParam) : false);
actionBar.addTab(tab2, savedlnstanceState == null ? Carro.TIPO_ LUX O.eq uals(tipoParam) : false);
actionBar.addTab(tab3, savedlnstanceState = null ? Carro.TIPO_ESPORTIVOS.equals(tipoParam) : false);
if (savedlnstanceState != null) {
// Restaura o estado
int tabldx = savedlnstanceState.getlnt(" tabldx " );
atualiz arTabs = false;
actionBar.setSelectedNavigationltem(tabldx );
}
}
(SOverride
protected void onSaveInstanceState(Bundle outState) {
super.onSavelnstanceState(outState);
int tabldx = getActionBar().getSelectedNavigationIndex ();
outState.putlnt(" tabldx " , tabldx );
}
gO verride
public boolean onO ptionsItemSelected(M enuItem item) {
switch (item.getltemldO ) {
case android.R.id.home:
// Volta para o incio
Intent intent = new Intent(this, Main.class);
intent. addF lags (Intent. F LAG_ ACTIVITY _ CLEAR_ TO P);
startActivity (intent);
return true;
}
return super.onO ptionsItemSelected(item);
}
// Listener para trocar a lista de carros ao escolher uma Tab
private class TabSelecionaCarroListener implements ActionBar.TabListener {
private F ragment frag;
public TabSelecionaCarroListener(F ragment frag) {
this.frag = frag;
}
Cap tulo 9 An droi d 4.x - Ice Cream San dwi ch 339
S
public void onTabSelected(Tab tab, android.app.F ragmentTransaction ft) {
if (! atualiz arTabs) {
Log.i(" l i v r o a n d r o i d > O K !");
frag = null;
} else {
if (frag != null) {
// Atualiz a o fragment com a lista de carros na esquerda
// U tiliz a a compatibility library
F ragmentTransaction t = getSupportFragment' !anager().beginTransaction();
t.replace(R.id.lay outEsq uerda, frag, null);
t.commit();
}
}
atualiz arTabs = true;
}
public void onTabU nselected(Tab tab, android.app.F ragsentTransaction ft) {
if (frag != null) {
// Remove utiliz ando a compatibility library
F ragmentTransaction t = getSupportF ragmentM anager().beginTransaction();
t.remove(frag);
t.commitQ ;
}
}
public void onTabReselected(Tab tab, android.app.F ragmentTransaction ft) {
}
}
}
Ao executar esse exemplo e girar a tela no emulador do Android 4.x, a Tab
selecionada ser mantida, assim como a lista de carros, como o esperado.
9.9 Travando a tela na horizontal para os tablets
Como a orientao na horizontal a mais utilizada nos tablets, vamos fixar essa
orientao somente quando a aplicao estiver executando em um tablet com
Honeycomb ou superior.
Isso poderia ser implementado utilizando-se o atributo android :screenO rientation
= " landscape" na configurao da activity no AndroidManifest.xml, mas como estamos
utilizando as mesmas activities para a verso smartphone e tablet, no podemos
fazer isso.
340
Google An droi d para T ablets
Outra maneira seria utilizar a classe O rientacaoU tils que criamos anteriormente,
para forar a orientao na horizontal, se o aparelho for um tablet com Android
3.x ou superior. *
if (Androidutils.isAndroid_ 3_ Tablet(this)) {
O rientacaoU tils.setO rientacaoH oriz ontal(this);
}
Essa uma boa soluo, e j aproveitamos para demonstrar mais um truque
de desenvolvimento. Portanto, vamos atualizar a classe LivroAndroidActivity , que
utilizada como base para cada activity do projeto fazer isso.
Li vroAn droi dActi vi ty.java
public class LivroAndroidActivity ex tends F ragmentActivity {
// Nada aqui, porq ue os fragments faz em tudo
// M as lembre-se q ue para a compatibility library funcionar
// necessrio herdar de F ragmentActivity
gO verride
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
if (Androidutils.isAndroid_ 3_ Tablet(this)) {
O rientacaoU tils.setO rientacaoH oriz ontal(this);
}
}
}
Pronto, agora a verso para tablets est com o layout fixo na horizontal, mas
deixamos a verso smartphone girar vontade.
Neste momento o projeto dos carros est 100% funcional, e tenho confiana
de que os conceitos apresentados neste projeto, apesar de simples, fornecem uma
base slida para o desenvolvimento de aplicaes para Android.
No prximo captulo vamos estudar a biblioteca de animaes, para depois
aplicar alguns efeitos especiais em algumas telas da aplicao.
CAPTULO 1 0
Animaes
Smartphones modernos, como o Android, possuem recursos sensacionais, como
animaes, que permitem criar efeitos especiais nas aplicaes para melhorar a
interatividade com o usurio.
Neste captulo vamos estudar diversos tipos de animaes, como, por exemplo,
fazer um objeto aparecer ou desaparecer, rotacionar, mover, aumentar ou diminuir
de tamanho e muito mais.
10.1 A classe Animation
As classes android. view. animation .Animation e android. view. animation. AnimationU tils formam
a dupla principal para se trabalhar com animaes no Android, para criar efeitos
especiais e aumentar o prestgio da aplicao.
A classe Animation possui cinco subclasses:
AlphaAnimation: Utilizada para criar o efeito de esconder ou aparecer, conhe-
cidos como os populares efeitos de fade_ in e fade_ out.
RotateAnimation: utilizada para rotacionar uma view controlando as coorde
nadas Xe Y.
ScaleAnimation: utilizada para escalar (aumentar ou diminuir) uma view.
TranslateAnimation: utilizada para mover uma view pela tela controlando as
coordenadas X e Y. Alguns feitos desse tipo comumente utilizados so o sli-
de_ left e o slide_ right, para fazer com que a view seja movida para a esquerda
ou a direita, respectivamente.
AnimationSet: classe especial utilizada para compor um grupo de animaes
para serem executadas ao mesmo tempo.
341
342
Google An droi d para T ablets
Para utilizar animaes, frequentemente utilizamos'o mtodo AnimationU tils.
loadAnimation(contex t, id),que recebe como argumento o identificador da animao
que precisa ser carregada.
Por exemplo, se precisarmos criar uma animao que vai esconder uma view
gradativamente, basta utilizar um simples trecho de cdigo conforme demons
trado a seguir:
View view = ?
Animation a = AnimationU tils.loadAnimation(this,android.R.anim.fadej n);
a.setDuration(2000);
view.startAnimation(a);
Esse mtodo utiliza a classe AnimationU tils para carregar uma animao. Note
que o parmetro passado um recurso de animao que pode ser acessado pela
famosa classe R. Nesse caso, utilizamos a prpria classe android. R nativa do Android
para acessarmos um recurso de animao j existente na plataforma. Mas nada
nos impede de criar nosso prprio recurso de animao e inserir o arquivo XML
na pasta /res/anim.
Note que a classe AnimationU tils utilizada para inflar o arquivo de animao
informado em um objeto correspondente. Neste exemplo estamos carregando
a animao android.R.anim.fadej n, e isso retorna um objeto do tipo AlphaAnimation.
Depois que uma animao criada podemos customizar a forma como esta
ser exibida. Neste exemplo foi chamado o mtodo setDuration(millis), que recebe
o tempo em milissegundos que o efeito da animao deve durar.
A seguir temos alguns dos principais mtodos da classe Animation:
M todo Descrio
setDuration(millis)
Configura o tempo em milissegundos que o efeito da animao
deve durar.
setF illAfter(boolean)
Este flag indica que o efeito da animao, conhecido como
transformao, deve persistir ao termino da execuo. O valor
padro false. Por exemplo, se criarmos uma animao de fade_
out em uma view, tal animao ir desaparecer aos poucos. Mas
ao final da animao a view vai voltar ao normal, aparecendo
novamente. Este mtodo pode ser chamado para que a view
permanea invisvel ao trmino da animao.
setlnterpolator(i)
Informa qual a implementao da interface Interpolator que ser
utilizada. O padro utilizar o Linearlnterpolator.
setRepeatCount(int)
Configura a quantidade de repeties que a animao deve efetuar.
O padro 0.
setRepeatM ode(int tipo)
Configura se a animao deve se repetir ou no. Este mtodo
frequentemente chamado para deixar uma animao em loop:
setRepeatM ode(Animation.INF INITE).
Cap tulo 10 An i maes
343
Depois que a ani mao foi cri ada podemos, chamar o mtodo View.
startAnimation(aniniation) para iniciar a animao, mas vamos deixar isso para o
prximo tpico, onde criaremos o nosso projeto para este captulo.
10.2 Criando o projeto para as animaes
Ento mos obra e vamos criar o projeto LivroAndroid-Capl0-Anim para testar as
animaes.
Nome do projeto: LivroAndroid-Capl0-Anim
Pa c o t e : br. l i vroandroi d .ani m
Criar activity: M ain
Verso: Android 1.6 ou 2.x
Neste projeto vamos utilizar um menu inicial para chamar todos os exemplos
que vamos desenvolver neste captulo. A seguir podemos visualizar a classe M ain
deste projeto com todas as activities que vamos criar.
Como ainda no temos essas classes, podemos deixar o cdigo comentado por
enquanto, ou podemos criar algumas classes vazias, apenas para o projeto compilar.
& Mai n .java
public class M ain ex tends ListActivity {
private static final String[ ] ops = new String[ ] {
"Alpha",
"Rotate",
"Scale" ,
"Translate" ,
" AnimationSet" ,
" AnimationListener" ,
" ViewF lipper" ,
" View Animada" ,
" Ex emplo Bug - Translate Anim, /** no prx imo captulo **/
" Sair" };
gO verride
public void onCreate(Bundle icicle) {
super. onCreate (icide);
int lay out = android.R.lay out.simple_ list_ item_ l;
Array Adapter< String> adaptador = new Array Adapter< String> (this, lay out,ops);
this.setListAdapter(adaptador);
}
344
Google An droi d para T ablets
@ 0verride
protected void onListItemClick (ListView 1, View v, int position, long id) {
switch (position) {
case 0:
startActivity (new Intent(this, AlphaAnim.class));
break;
case 1:
startActivity (new Intent(thiSj RotateAnim.class));
break;
case 2:
startActivity (new Intent(this, ScaleAnim.class));
break;
case 3:
startActivity (new Intent(this, TranslateAnim.class));
break;
case 4:
startActivity (new Intent(this, Ex emploAnimationSet.class));
break;
case 5:
startActivity (new Intent(this, Ex emploAnimationListenerApagarTela.class));
break;
case 6:
startActivity (new Intent(this, Ex emploViewF lipperLogin.class));
break;
case 7:
startActivity (new Intent(this, Ex emploViewAnimada.class));
break;
case 8:
startActivity (new Intent(this, TranslateAnimBotaoBug.class));
break;
default:
finishQ;
}
}
}
Todas essas activities vo precisar estar declaradas no AndroidManifest.xml, ento
podemos ir adicionando uma a uma, conforme a necessidade, ou deixar todas
elas criadas com um cdigo vazio. Faa sua escolha. Para efeito de comparao
podemos visualizar o arquivo AndroidManifest.xml final que vamos desenvolver.
Cap tulo 10 An i maes
345
An droi dMan i f est.xml
<? xml version= ,,l 0encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.com/apk /nes/android"
pack age= " br.livroandroid. anim"
android:versionCode= " l"
android: versionName= " l.0">
< uses-sdk android:minSdk Version= " 4" />
< application android :icon= " @ drawable/iconandroid :label= " @ string/app_ name"
android:theme= (Bandroid:sty le/Theme.Light">
ctivity android:name= " .M ain"
android:label= " (3string/appjiame">
<intent-filter>
<action android:name= " android.intent.action.M AIN" />
< category android:name= android.intent.category .LAU NCH ER" />
</intent-filter>
</activity >
< activity android:name='' .AlphaAnim" />
< activity android:name= " .RotateAnim" />
< activity android:name=" .ScaleAniii" />
< activity android:name= " .TranslateAnim" />
(activity android :name=''.ExemploAniniationSet" />
< activity android:name= " Ex emploAnimationListenerApagarTela" />
activity android:name= " .Ex emploViewF lipperLogin" />
< activity android:name= ' ' .Ex emploViewAnimada" />
< activity android:name= " .TranslateAnimBotaoBug" />
< /application>
</manifest>
No decorrer deste captulo vamos criar cada activity para demonstrar as
animaes, e todas elas vo utilizar o mesmo arquivo de layout. Vamos alterar
somente as animaes. Portanto, vamos criar o arquivo de layout ex emplo_ animacao.
x ml para brincarmos com as animaes.
& /res/layout/exem p lo_a n i ma ca o.xm I
< ? xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " verticar'
android:lay out_width= "fill_ parent" .
android:lay out_height=" fill_parent"
android: background=''#eeeeee''
346
Google An droi d para T ablets
< LinearLay out
android :orientation= "horiz ontal' '
android: layout_width="fill_parent"
android:lay out_ height= " wrap_ content"
android:gravity = " center"
>
<8utton
android :.id="@+id/btAnimarXHL"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " Animar com XML'
android:lay out_ gravity = " center"
android:onClick = " onClick AnimarX H L"
/>
< Button
android: id=''(S+id/btAnimarAPI'
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " Animar com API"
android:lay out_ gravity = " center"
android:onClick = " onClick AnimarAPI"
/>
< /LinearLay out>
clmageView
android:id= " @ + id/img"
android:lay out_ width= " wrap_ content
android:lay out_ height= " wrap_ content"
android:src= " @ drawable/android"
android:lay out_ gravity = " center"
/>
< /LinearLay out>
Essa tela vai simplesmente exibir dois botes para iniciar a animao, alm
de uma imagem, a qual vai receber o efeito da animao. Voc pode encontrar a
imagem do bonequinho do Android no link de download com os projetos do livro.
O primeiro boto vai iniciar a animao utilizando um arquivo em XML, e o
segundo vai utilizar a API J ava.
Repare que cada boto declara um mtodo para ser executado utilizando a
propriedade android :onClick , que permite que se execute um mtodo diretamente na
activity, sem a necessidade de recuperar o boto e adicionar o evento manualmente.
< Button
Cap tulo 10 An i maes
347
android: id="@ +id/btAniniarX Hl"
android:tex t= " Animar com XML"
android:onClick = " onClick AnimarX M L" />
< Button
android: id="j3+id/btAniniarAPI"
android:tex t= 11 Animar com API"
android: onClick = onClickAniniarAPI" />
A assinatura desses mtodos simples e recebe como parmetro a view que
originou o evento.
public void onClick AnimarX M L(View v) {
// Animao em X M L aqui
}
public void onClick AnimarAPI(View v) {
// Animao com a API Dava aqui
}
9S
O atributo android :onClick utilizado para chamar os mtodos
automaticamente foi adicionado a partir do Android 1.6.
A figura 10.1 exibe o resultado desse layout que acabamos de criar.
Figiira 10.1 - Tela de exemplo para as animaes.
348
Google An droi d para T ablets
10.3 AlphaAnimation
A classe AlphaAnimation permite criar os famosos efeitos de fade_ in e fade_ out, para
fazer com que uma view seja exibida ou escondida, criando um efeito amigvel
que pode ser configurado para durar X milissegundos.
Essa animao troca a propriedade alpha da view, que a propriedade que
controla a intensidade da cor. Sempre que o alph for 0.0, significa que a view est
invisvel, e sempre que o alpha for 1.0, ela est 100% visvel e com sua intensidade
mxima de cor. A propriedade alpha um float que varia entre 0.0 e 1.0.
A propriedade alpha muito utilizada por designers de diversas
aplicaes para criar imagens com transparncias.
A tabela a seguir resume os dois parmetros utilizados pela classe AlphaAnimation.
Propriedade Descrio
float fromAlpha
Valor inicial da propriedade alpha.
float toAlpha Valor final da propriedade alpha.
Vamos prosseguir com o exemplo e criar nosso primeiro efeito de animao,
com o objetivo de fazer com que uma imagem desaparea da tela fade_ out utilizando
uma animao de dois segundos, de forma que a transparncia da imagem seja aos
poucos aumentada, at que ela fique invisvel por completo. Depois que a imagem
ficar invisvel vamos fazer com que ela aparea novamente aos poucos (fade_ in.)
Para este exemplo vamos controlar se a imagem est visvel ou invisvel com
um flag dentro da classe, que ser utilizado toda vez que for acionado o boto
para executar uma animao com o efeito inverso da anterior. Por exemplo, se a
imagem est visvel, vamos aplicar o efeito fade_ out para escond-la, e na prxima
vez vamos aplicar o efeito fade_ in para exibi-la novamente.
& AlphaAn i m.java
public class AlphaAnim extends Activity {
private boolean visivel = true;
(verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View v) {
int anim = visivel ? android.R.anim.fade_ out : android.R.anim.fade_ in;
Animation a = AnimationU tils.loadAnimation(this, anim);
Cap tulo 10 An i maes 349
animar(a);
}
public void onClick AnimarAPI(View v) {
AlphaAnimation fade_ out = new AlphaAnimation(1.0f, 0.0f);
AlphaAnimation fade_ in = new AlphaAnimation(0.0f, 1.0f);
AlphaAnimation a = visivel ? fade_ out : fade_ in;
animar(a);
}
public void animar(Animation a) {
// Dois segundos
a.setDuration(2000);
// M anter o efeito no final da animao
a.setF illAfter(true);
// Inicia a animao
ImageView img = (ImageView) findViewByld(R.id.img);
img.startAnimation(a);
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
visivel = ! visivel;
}
}
Este exemplo bem simples e vai aplicar os efeitos fade_ in e fade_ out conforme
o esperado. No livro meio complicado de se visualizar a animao, ento o re
comendado que voc acompanhe cada exerccio e os execute em um emulador
ou em um aparelho real com Android.
Mas para dar uma ideia do efeito da animao, a figura 10.2 exibe o resultado
deste exemplo. Podemos ver que a imagem est bastante transparente, pouco
tempo antes de desaparecer, devido ao efeito da animao fade_ out.
Para explicar a animao vamos primeiramente analisar o mtodo que utiliza
um arquivo XML.
public void onClick AnimarX M L(View v) {
int anim = visivel ? android.R.anim.fade_ out : android.R.anim.fade_ in;
Animation a = AnimationU tils.loadAnimation(this, anim);
animar(a); _
}
Note que os recursos de animao utilizados pertencem classe android.R nativa
do Android. Dessa forma, podemos carregar as animaes fade_ in e fade_ out com
o mnimo de esforo. Simples, no?
350
&
Google An droi d para T ablets
Figiira 10.2 - Imagem quase desaparecendo durante o efeito da animao fadejMt.
Mas e se quisermos customizar a animao e criar nosso prprio XML? Isso
simples! Para isso podemos criar um arquivo XML vlido para animaes e
coloc-lo na pasta /res/anim.
Ento vamos colocar a mo na massa e criar um arquivo XML customizado
para uma animao de fade_ in e fade_ out com o tempo padro de durao de cinco
segundos.
& /res / an i m / f a d e _i n . xm l
< ? x ml version= " 1.0" encoding= " utf-8" ? >
<!-- Esta animao faz a propriedade alpha ir do 0.0 invisvel para 1.0 visvel. -->
< alpha x mlns:android= " http.' //schemas.android.com/apk /res/android"
android: fromAlpha= 0.0"
android:toAlpha= " 1.0
android:duration= " 5000"
/>
& /res / an i m / f a d e _o u t . xm l
< ? x ml version= " 1.0" encoding= " utf-8" ? >
<!-- Esta animao faz a propriedade alpha ir do 1.0 visvel para 0.0 invisvel. -->
< alpha x mlns:android= http://schemas. android.com/apk /res/android"
android:fromAlpha= " 1.0" .
android:toAlpha= 0 .0"
android:duration= " 5000"
/>
Cap tulo 10 An i maes
351
Neste momento podemos utilizar a classe R do nosso prprio projeto para
acessar esses recursos na pasta /res/anim. Note que no cdigo a seguir estamos
escrevendo apenas R, em vez de android.R.
public void onClick AnimarX M L(View v) {
int anim = visivel ? R.anim.fade_ out : R.anim.fade_ in;
Animation a = AnimationU tils.loadAnimation(this, anim);
animar(a);
}
O recomendado para uma melhor separao da lgica de negcios com os
recursos da aplicao sempre criarmos a animao utilizando os arquivos XML,
como fizemos at este momento. Mas s vezes necessrio que se criem animaes
dinamicamente utilizando a API J ava, e para isso a classe AlphaAnimation pode ser
utilizada diretamente pela API. Foi isso que fizemos no segundo mtodo.
public void onClick AnimarAPI(View v) {
AlphaAnimation fade_ out = new AlphaAnimation(1.0f, 0.0f);
AlphaAnimation fade_ in = new AlphaAnimation(0.0f, 1.0f);
AlphaAnimation a = visivel ? fade_ out : fade_ in;
animar(a);
}
Neste caso estamos utilizando a classe AlphaAnimation, passando os parmetros
fromAlpha e toAlpha no seu construtor, que so os mesmos que informamos ao criar
essa animao utilizando XML.
E recomendado que se criem as animaes utilizando arquivos de
XML dentro da pasta /res/anim. Lembre-se que podemos encontrar
vrias animaes j disponveis de forma nativa no Android,-.
dispensando a criao de arquivos de animao customizados.
Por exemplo, as animaes fade_ in e fade_ out j esto presentes
no Android, e para isso basta utilizar a classe android.R para ter
acesso a esses recursos nativos. Dentro da pasta /android-sdk.
platforms/${plataformaj/data/res/anim voc pode verificar o cdigo-;
fonte dos arquivos XML das animaes disponveis e neles se basear
caso seja necessrio fazer alguma alterao.
10.4 RotateAnimation
A classe RotateAnimation utilizada para rotacionar uma view dentro da tela, onde
as coordenadas Xe Y so levadas em considerao e um ngulo em graus utili
zado para a rotao.
Para essa animao possvel configurar um ponto que ser o eixo da rotao.
O padro da rotao a partir do canto superior esquerdo da view, que possui
os pontos x = 0 e y = 0.
A tabela a seguir resume os dois parmetros utilizados pela classe RotateAnimation.
352 Google An droi d para T ablets
Propriedade ... ( Descrio
float fromDegrees Valor inicial do ngulo para rotacionar.
float toDegrees Valor final do ngulo para rotacionar.
int pivotX Ty pe
Tipo da rotao aplicada no eixo X, podendo assumir uma das trs
constantes: Animation.ABSO LU TE, Animation.RELATIVE_ TO _ SELF ou Animation.
RELATIVE TO PARENT.
float pivotX Value
Valor no eixo X para servir de eixo da rotao. Pode receber um valor
absoluto em pixels, onde 0 representa o canto esquerdo superior ou
valores relativos coordenada da view ou de seu layout pai.
int pivotY Ty pe
Tipo da rotao aplicada para o eixo Y, podendo assumir uma das trs
constantes: Animation.ABSO LU TE, Animation.RELATIVE_ TO _ SELF ou Animation.
RELATIVE TO PARENT.
float pivotY Value
Valor no eixo Y para servir de eixo da rotao. Pode receber um valor
absoluto em pixels, onde 0 representa o canto esquerdo superior ou
valores relativos coordenada da view ou de seu layout pai.
Para demonstrar a classe RotateAnimation vamos criar uma animao para rota
cionar a imagem do bonequinho em 180, onde o eixo da rotao o centro da
imagem.
i #i RotateAn i m.java
public class RotateAnim ex tends Activity {
private boolean flag = true;
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View v) {
Animation girai = AnimationU tils.loadAnimation(this, R.anim.rotate_ gira_ ponta cabeca);
Animation gira2 = AnimationUtils.loadAnimation(this, R.anim.rotate_gira_ponta_cabeca retorno);
Animation a = flag ? girai : gira2;
animar(a);
}
public void onClick AnimarAPI(View v) {
int angulo = 180;
Cap tulo 1 0 An i maes 353
Animation girai = new RotateAnimation(0, angulo,
Animation. RELATIVEJ O J ELF , 0.5F,
Animation. RELATIVEJ O J ELF , 0.5F);
Animation gira2 = new RotateAnimation(angulo, 0,
Animation.RELATIVEJ O J ELF , 0.5F,
Animation. RELATIVEJ O J ELF , 0.5F );
Animation a = flag ? girai : gira2;
animar(a);
}
public void animar(Animation a) {
// Dois segundos
a.setDuration(2000);
// M anter o efeito no final da animao
a.setF illAfter(true);
// Inicia a animao
ImageView img = (ImageView) findViewBy ld(R.id.img);
img.startAnimation(a);
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
flag = ! flag;
}
}
Para a animao em XML vamos criar dois arquivos conforme demonstrado a
seguir. O ngulo de rotao vai de 0o 180, para dar um giro completo, e o eixo da
rotao foi definido no centro da imagem, conforme as propriedades android ipivotx
e android: pivotY , que no XML podem receber o valor de 50%.
Tambm ser criado o arquivo XML para fazer a rotao voltar. Ento pri
meiramente vamos girar a imagem de ponta-cabea, e depois vamos gir-la no
sentido contrrio para retornar posio original.
j / res/a n i m / r o ta te _ g i ra_pon ta_cabeca .xml
< ? xml version= 1.0" encoding= " utf-8" ? >
<! -- Rotao do ngulo 0o para 180 -->
< rotate x mlns:android= http://schemas.android.com/apk /res/android"
android:fromDegrees= " 0"
android:toDegrees= " 180"
android:pivotx = " 50 "
android :pivotY=''50%"
android: duration= " 5000
android:fillAfter=" true"
354 Google An droi d para T ablets
iS d /res/an i m/rotate_gi ra_pon ta_cabeca_retorn o.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
<!-- Rotao do ngulo 180 para 0o -->
< notate x mlns:android= " http://schemas.android.com/apk /res/android"
android:fromO egrees= " 180"
android:toDegrees= " 0"
android:pivotX = " 50% "
android :pivotV="50"
android :duration="5000"
android :fillAfter=true"
/>
A figura 103 exibe o resultado deste exemplo e mostra o bonequinho do An
droid de ponta-cabea depois da rotao.
... . a B einwi
Figura 10.3 - Exemplo da imagem rotacionada em 180.
Neste exemplo tambm criamos a mesma animao utilizando a API J ava, e
para isso utilizamos a classe RotationAnimation.
public void onClick AnimarAPI(View v) {
int angulo = 180;
Animation girai = new RotateAnimation(0,angulo,
Animation.RELATIVE_ TO _ SELF , 0.5F,
Animation.RELATIVE_ TO _ SELF , 0.5F );
Animation gira2 = new RotateAnimation(angulo,0,
Animation.RELATIVE_ TO _ SELF , 0.5F,
Animation.RELATIVE_ TO _ SELF , 0.5F );
Cap tulo 10 An i maes 355

Animation a = flag ? girai : gira2;


animar(a);
}
Note que foi passado no construtor da classe RotationAnimation o grau inicial e o
final da rotao (0 e 180) e as coordenadas X e Y, que so o eixo da rotao. Para a
animao de retorno foi definido o grau inicial e o final da rotao como 180 e 0o.
Foi informado Animation.RELATIVE_ T0_ SELF para indicar que a coordenada relativa
ao prprio objeto e o valor 0.5F, que correspondente a 50%. Nesse caso, o valor
0.0F igual a 0%, 0.5F igual a 50% e 1.0F igual a 100%.
protected void giraVoltaCompletaPlQ {
RotateAnimation a = new RotateAnimation(0,360,
Animation.RELATIVE_ T0_ 5ELF , 0.5F,
Animation.RELATIVE_ T0_ SELF , 0.5F);
a.setDuration(5000);
ImageView img = (ImageView) findViewById(R.id.img)j
img.startAnimation(a);
}
A constante Animation.RELATIVE_ T0_ SELF utilizada para indicar que a rotao
relativa ao objeto que est sendo animado. Dessa forma sabemos que os pontos
X e Y que indicamos com o valor 0.5F (50%) so o centro da imagem. Se alterar
mos essa constante para Animation.RELATIVE_ T0_ PARENT, esse ponto de eixo passaria
a ser o centro da tela. A diferena na animao seria gritante. Ao rotacionar em
seu prprio eixo a imagem no muda de lugar, apenas gira. Mas se a view for
rotacionada utilizando outro ponto como centro da tela, ela iria se movimentar
ao redor desse ponto.
interessante que voc brinque bastante alterando os parmetros do eixo da
rotao para que entenda bem o funcionamento das animaes.
Aoutilizaranotaodepercentual(exemploandroid:pivotX=100%)
no XML de animao ela vai, por padro, considerar a configurao
relativa view, como se tivesse utilizado Animation.RELATIVE_ T0_ SELF .
Para fazer com que esse parmetro seja considerado relativo ao pai
da view deve-se utilizar um p depois do sinal de percentual. Por
exemplo, android :pivotX = " 100%p". Dessa forma, seria como se tivesse
utilizado Animation.RELATIVE_ T0_ PARENT pela API.
356
Google An droi d para T ablets
10.5 ScaleAnimation
A classe ScaleAnimation permite aplicar animaes que vo diminuir ou aumentar
uma view.
O exemplo que vamos criar vai encolher a view at que ela fique to pequena
que no aparea mais. E na prxima vez vamos fazer com que ela aumente nova
mente, gradativamente, at o seu tamanho normal.
Neste exemplo vamos manter um flag para controlar qual animao deve ser
realizada, da mesma forma que fizemos no exemplo do AlphaAnimation. Portanto,
vamos exibir o cdigo-fonte completo da classe.
! i ScaleAn i m.java
public class ScaleAnim ex tends Activity {
private boolean visivel = true;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View v) {
int anim = visivel ? R.anim.scale_ diminuir : R.anim.scale_ aumentar;
Animation a = AnimationU tils.loadAnimation(this, anim);
animar(a);
}
public void onClick AnimarAPI(View v) {
ScaleAnimation encolher = new ScaleAnimation(
1.0f, 0.0f, // X inicial e final
1.0f, 0.0f, // Y inicial e final
Animation.RELATIVEJ O J ELF , 0.5f, // Eixo X
Animation.RELATIVEJ O J ELF , 0.5f // Eix o Y
);
ScaleAnimation aumentar = new ScaleAnimation(
0.0f, 1.0f, // X inicial e final
0.0f, 1.0f, // Y iniciare final
Animation.RELATIVEJ O J ELF , 0.5f, // Eix o X
Animation.RELATIVEJ O J ELF , 0.5f // Eixo Y
);
Animation a = visivel ? encolher : aumentar;
animar(a);
}
Cap tulo 1 0 An i maes 357
public void animar(Animation a) {
// Dois segundos
a.setDuration(2000);
// M anter o efeito no final da animao
a .setF illAfter(true);
// Inicia a animao
ImageView img = (ImageView) findViewByld(R.id.img);
img. startAnimation(a);
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
visivel = !visivelj
}
}
Assim como nos demais exemplos, foram criados dois mtodos, um para criar
a animao a partir de um arquivo XML e outro utilizando a API.
Vamos comear e verificar como criar a animao de diminuir e aumentar em
XML, conforme podemos visualizar a seguir.
A animao criada pelo arquivo /res/anim/scale_dvninuir.xml define que as coor
denadas iniciais da animao so 1.0 (100%) tanto de Xquanto de Y, informando
o tamanho total e original da imagem. E as coordenadas finais informadas so
0.0 (0%), informando que o tamanho final da imagem deve ser reduzido em nada.
Como o eixo da animao foi definido como 50%, a imagem vai diminuir aos
poucos at desaparecer no seu centro.
< ) /res/an i m/scale_di mi n ui r.xml
< ?x ml version= " 1.0" encoding= " utf-8?>
cale x mlns:android= " http://schemas.android.com/apk /res/android"
android:fromX Scale= " 1.0" android:toX Scale= 0.0"
android:fromY Scale= " l.0" android:toY Scale= " 0.0"
android:pivotX= "50)"
android:pivotY= "503!"
android:fillAfter= "true"
android:duration= " 10000"
/>
A animao criada pelo arquivo /res/anim/scale_aumentar.xml o contrrio da
outra. As coordenadas iniciais so 0.0, indicando que a imagem no existe, pois
est com seu tamanho zerado. Dssa forma, as coordenadas finais foram definidas
como 1.0 (100%), para que a imagem volte ao seu tamanho original e total. O eixo
de partida da animao foi definido como 50%, tambm para que a animao
comece no centro da imagem.
& / res/an i m/scale_aumen tar.xml
358
Google An droi d para T ablets
\
< ?xml version= " 1.0" encoding= " utf-8" ? >
< scale x mlns:android= " http://schemas.android.com/apk /res/android"
android:fromX Scale= " 0.0" android:toX Scale= 1.0
android:fromY Scale= " 0.0" android:toY Scale= " 1.0"
android:pivotX = " 50% "
android:pivotY = 58%"
android:fillAfter= "true"
android:duration= " 10600"
/>
Ao executar esse exemplo a imagem vai diminuir at desaparecer, e na prxima
vez vai aumentar gradativamente at ficar com o tamanho original. Na figura 10.4
podemos visualizar a imagem durante a animao, bem pequena, quase antes de
desaparecer por completo.
a
W
Figura 10.4 - Exemplo da imagem diminuindo de tamanho.
Da mesma forma que as outras animaes, tambm podemos criar a animao
de escalar utilizando a API. Para isso usamos a classe ScaleAnimation e informamos os
parmetros no construtor da classe, da mesma forma que fizemos no arquivo XML.
Note que os valores 0.0F e 1.0F representam 0% e 100%, respectivamente.
public void onClick AnimarAPI(View v) {
ScaleAnimation encolher = new ScaleAnimation(
1.0f, 0.0fj // X inicial e final
1.0f, 0.0f, // Y inicial e final
Animation.RELATIVE_ T0_ 5ELF j 0.5f, // Eix o X
Cap tulo 10 An i maes
359
Animation.RELATIVEJOJELF, 0.5f // Eixo Y
);
ScaleAnimation aumentar =newScaleAnimation(
0.0f, 1.0f, // X inicial e final
0.0f, 1.0f, // Y inicial e final
Animation.RELATIVEJOJELF, 0.5f, // Eixo X
Animation.RELATIVEJOJELF, 0.5f // Eixo Y
);
Animation a =visivel ? encolher : aumentar;
animar(a);
}
10.6TranslateAnimation
A classe TranslateAnimation utilizada para mover uma view pela tela especificando
as coordenadas x e Y iniciais e finais da animao. Essas coordenadas podem ser
passadas como um valor absoluto absol ute em pixels de tela, ou relativo prpria
view RELATIVEJ OJ ELF ou ao seu layout pai RELATIVEJO_PARENT.
A tabela a seguir resume os parmetros utilizados pela classe TranslateAnimation.
Propriedade Descrio
float fromXType Tipo da coordenada X inicial.
float fromXValue Valor da coordenada X inicial.
int toXType Tipo da coordenada X final.
float toXValue Vaior da coordenada X final.
int fromYType Tipo da coordenada Y inicial.
float fromYValue Valor da coordenada Y inicial.
int toYType Tipo da coordenada Y final.
float toYValue Valor da coordenada Y final.
Basicamente, a classe recebe as coordenadas X e Y iniciais e finais. Dessa forma,
temos quatro pontos. Para cada ponto especificado qual o tipo do valor, que
pode ser Animation.ABSOLUTE, Animation.RELATIVEJ OJ ELF OU Animation.RELATIVEJO_PARENT.
Sempre que o tipo de um valor for Animation.ABSOLUTE, aquele parmetro poder
receber valores absolutos, que so valores numricos especificando a real posio
X ou Y em pixels.
Se O tipo do parmetro for Animation. RELATIVEJ OJ ELF OU Animation. RELATIVE J0_PARENT,
os valores sero informados em percentual, sendo que no XML so utilizados os
valores 0% e 100%, e no J ava so utilizados os valores 0.0F e 100.0F.
360
Google An droi d para T ablets
O exemplo que vamos criar vai mover a imagem para baixo e depois para cima.
l T ran slateAn i m.java
public class TranslateAnim ex tends Activity {
private boolean visvel = true;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View v) {
int anim = visivel ? R.anim.translate_ mover_ para_ baix o : R.anim.translate_ mover_ para_ cima;
Animation a = Animationlltils.loadAnimation(this, anim);
animar(a);
}
public void onClick AnimarAPI(View v) {
Animation moverParaBaix o = new TranslateAnimation(
Animation.REU TIVE_ TO _ SELF , 0.0F, Animation.RELATIVE_ TO _ SELF , 0.0F,
Animation.RELATIVE_ TO _ SELF , 0.0F, Animation.RELATIVEJ O J ELF , 2.0F
);
Animation moverParaCima = new TranslateAnimation(
Animation.RELATIVE_ TO _ SELF j 0.0F, Animation.RELATIVEJ O J ELF , 0.0F,
Animation.RELATIVE_ TO _ SELF , 2.0F, Animation.RELATIVE_ TO _ SELF j 0.0F
);
Animation a = visivel ? moverParaBaix o : moverParaCima;
animar(a);
}
public void animar(Animation a) {
ImageView img = (ImageView) findViewByld(R.id.img);
// Dois segundos
a.setO uration(2000);
// M anter o efeito no final da animao
a.setF illAfter(true);
img.startAnimation(a);
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
visivel = !visivel; ~
}
}
Vamos comear primeiro pela explicao da animao em XML, que vai mover
a imagem para baixo e para cima.
A animao criada pelo arquivo /res/anim/translate_mover_para_baixo.xml define
que as coordenadas iniciais da animao so 0.0 (0%) tanto dg Xquanto de Y.
Cap tulo 10 An i maes 361
Note que somente a coordenada final Y foi deslocada em 200%. Nesse tipo de
animao, 100% representa o tamanho da imagem. Sendo assim, 200% indica
que a imagem vai ser movida para baixo em duas vezes o seu tamanho.
& /res/an i m/tran slate_mover_para_bai xo.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0.0" android:toXDelta="0.0"
android:fromYDelta="0.0" android:toYDelta="200%"
android:fillAfter="true"
android:duration="10000"
/>
A animao criada pelo arquivo lreslanimltranslate_mover_para_ma.xml o
contrrio da outra, e as coordenadas finais do eixo Y esto invertidas, para que a
imagem seja movida para cima, para voltar sua posio original.
S /res/an i m/tr an sIate_mover_para_ci ma.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0.0" android:toXDelta="0.0"
android:fromYDelta="200%" android:toYDelta="0.0
android:fillAfter="true"
android:duration="10000"
/>
Lembre-se que no arquivo de animao em XML os valores em
percentuais so sempre relativos view que est sendo animada.
Se o desejado for informar um valor relativo view pai, pode
ser informado o valor com um 'p' no final, como, por exemplo,
android:toYDelta="50%p".
Ao executar esse exemplo a imagem vai mover para baixo numa distncia de
duas vezes o seu tamanho, devido configurao android:toYDelta="200%". Depois, na
prxima vez, a imagem vai ser movida para cima at voltar sua posio original.
Na figura 10.5 podemos visualizar a imagem durante a animao, quando ela
foi movida para baixo. Para facilitar o entendimento do exemplo, essa figura foi
alterada para exibir tanto a imagem antes da animao quanto a imagem embaixo
depois da animao. Note que o espao entre as duas imagens exatamente igual
ao tamanho de uma imagem, comprovando que a imagem foi movida em 200%,
isto , duas vezes o seu tamanho.
362
Google An droi d para T ablets
Figura 10.5 - Exemplo da imagem depois de se mover para baixo.
10.7 AnimationSet
A classeAnimationSet permite agrupar vrias animaes para que sejam executadas ao
mesmo tempo. A maneira de utilizar simples. Basta criar um objeto AnimationSet e
adicionar as animaes que devem executar. Falando em um portugus bem claro,
essa classe uma lista de animaes, para que sejam reproduzidas simultaneamente.
A classe AnimationSet tambm possui os mtodos para configurar as proprie
dades das animaes, como, por exemplo, o mtodo setDuration(long), para alterar
o tempo de execuo. Se alguma propriedade for alterada nessa classe, ela ir
sobrescrever as propriedades que foram antes configuradas em cada animao
que foi adicionada lista.
Para facilitar o entendimento vamos criar um exemplo, o qual vai juntar a ani
mao que apagava fade_ out e aparecia f ade_ in com a animao que movimentava
um objeto para baixo e para cima. Dessa forma estaremos juntando as animaes
AlphaAnimation e TranslateAnimation e um nico objeto AnimationSet.
i ExemploAn i mati on Set.java
public class Ex emploAnimationSet ex tends Activity {
private boolean flag = true;
(W verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
Cap tulo 10 An i maes
363
setContentView(R.lay out.ex emplo_ animacao);
}
// Animar pelo X M L
public void onClick AnimarX M LfView v) {
AnimationSet setl =
(AnimationSet) AnimationU tils.loadAnimation(this, R.anim.set_ mover_ para_ baix o_ desaparecer);
AnimationSet set2 = (AnimationSet) AnimationU tils.loadAnimation(this,
R.anim.set_ mover_ para_ cima_ aparecer);
AnimationSet set = flag ? setl : set2;
animar(set);
}
// Animar pela API
public void onClick AnimarAPI(View v) {
AnimationSet lista = new AnimationSet(true);
Animation al = getAnimacaoM overParaBaix oCimaQ ;
Animation a2 = getAnimacaoAparecerDesaparecer();
lista.addAnimation(al);
lista.addAnimation(a2);
animar(lista);
}
// Animar o obj eto criado
public void animar(AnimationSet lista) {
ImageView img = (ImageView) findViewByld(R.id.img);
// Dois segundos
lista.setDuration(2000);
// M anter o efeito no final da animao
lista.setF illAfter(true);
img.startAnimation(lista);
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
flag = Iflag;
}
// Animao para mover para baix o e para cima
private Animation getAnimacacM overParaBaix oCimaQ {
int anim = flag ? R.anim.translate_ mover_ para_ baix o : R.anim.translate_ mover_ para_ cima;
Animation a = AnimationU tils.loadAnimation(this, anim);
return a;
}
// Animao para esconder e aparecer
protected Animation getAnimacaoAparecerDesaparecer() {
int anim = flag ? R.anim.fade_ out : R.anim.fade_ in;
Animation a = AnimationU tils.loadAnimation(this, anim);
return a;
}
}
364
Google An droi d para T ablets
Da mesma forma que nos demais exemplos, foram criados dois mtodos para
obter a animao, um utilizando XML e outro utilizando a API.
Note que criar o AnimationSet simples. Basta adicionar as animaes desejadas
utilizando o mtodo addAnimation(animation). Como a classe AnimationSet filha de
Animation, o mtodo View.startAnimation(animation) pode continuar sendo usado tran
quilamente. A diferena agora que, ao chamar esse mtodo, vrias animaes
vo acontecer simultaneamente.
public void onClick AnimarAPI(View v) {
AnimationSet lista = new AnimationSet(true)j
Animation al = getAnimacaoM overParaBaix oCima();
Animation a2 = getAnimacaoAparecerDesaparecerQ ;
lista.addAnimation(al)j
lista.addAnimation(a2);
animar(lista);
}
O resto do cdigo igual aos anteriores, onde simplesmente criamos as anima
es desejadas e configuramos alguns parmetros, como, por exemplo, o tempo
de animao com o mtodo setDuration(long) e, para persistir o estado da animao
no final, o mtodo setF illAfter(boolean).
Outra possibilidade criar o AnimationSet utilizando diretamente o XML, onde
as tags de cada animao so agrupadas dentro de uma tag < set> .
A seguir temos o mesmo conjunto de animaes criadas pela API, mas desta
vez diretamente no XML.
A animao criada pelo arquivo /res/anim/set_mover_para_baixo_desaparecer.xml
faz com que a view mova para baixo e desaparea ao mesmo tempo.
i ) /res/an i m/set_mover_para_bai xo_desaparecer.xml
< ? xml version= " 1.0" encoding= utf-8" ? >
<set x mlns:android= " http://schemas.android.com/apk /res/android">
< translate
android:fromX Delta= " 0.0" android:toX Delta= " 0.0"
android:fromY Delta= " 0.0" android:toY Delta= "200S"
android :fillAfter="true"
android:duration= " 10000"
/>
1
Cap tulo 1 0 An i maes 365
<alpha
android :froiAlpha="l. 0"
android:toAlpha= 0.0" *
android:duration= " 5000"
/>
</set>
A animao criada pelo arquivo /res/anim/set_mover_para_cima_aparecer.xml faz
com que a view mova para cima e aparea ao mesmo tempo.
/res/an i m/set_mover_para_ci ma_aparecer.xml
<? xml version= 1.0" encoding= " utf-8" ? >
< set xir.lns:android= " http://schemas.android.com/apk /res/android>
<translate
android:fromX Delta= " 0.0" android:toX Delta= " 0.0
android:fromVDelta= " 200% " android:toY Delta= " 0.0"
android :fillAfter="true
android :duration= " 1000011
/>
<alpha
android:fromAlpha= " 0.0"
android:toAlpha= " 1.0
android:duration= " 5000"
/>
</set>
Agora podemos carregar o AnimationSet diretamente utilizando esses recursos
de animao.
public void onClick AnimarX M L(View v) {
AnimationSet setl = (AnimationSet) AnimationU tils.loadAnimation(this,
R.anim.set_ Diover_ para_ baix o_ desaparecer);
AnimationSet set2 = (AnimationSet) AnimationU tils.loadAnimation(this,
R.anim.set_ mover_ para_ cima_ aparecer);
AnimationSet set = flag ? setl : set2;
animar(set);
} ~
Lembre-se que as animaes adicionadas no AnimationSet sero agrupadas em
uma nica animao. Os efeitos e transformaes de cada animao vo acontecer
ao mesmo tempo, o que pod resultar em diferentes animaes. E interessante
que voc crie alguns exemplos para se acostumar com o comportamento da API.
A figura 10.6 exibe o resultado desta animao.
366
Google An droi d para T ablets
Figura 10.6 - Exemplo de AnimationSet.
10.8 AnimationListener
Dependendo do caso, necessrio monitorar o incio e o trmino de uma anima
o. Para isso podemos utilizar a interface AnimationListener.
A seguir podemos verificar os mtodos disponveis dessa interface.
public static interface AnimationListener {
// Indica que a animao foi iniciada
void onAnimationStart(Animation animation);
// Indica que a animao terminou
void onAnimationEnd(Animation animation);
// Indica que a animao est repetindo
void onAnimationRepeat(Animation animation);
}
Uma das utilizaes mais freqentes dessa interface para monitorar o final
de uma animao para executar alguma tarefa, como, por exemplo, iniciar outra
animao ou trocar de tela.
Vamos dizer que precisamos criar um efeito especial na aplicao antes de trocar
de tela, de forma que a tela atual seja gradativamente apagada fade_ out e somente
depois a nova tela seja exibida. Assim, criamos um pequeno efeito de transio
entre telas. Para fazer isso podemos aplicar uma animao do tipo AlphaAnimation
em toda a tela diretamente na view principal do layout.
Cap tulo 10 An i maes
Ento mos obra e vamos criar um simples formulrio de login para utilizar
como exemplo.
& /res/layout/logi n .xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android: lay out_ width= "fill j a r e n t "
android:layout_height= "fill_ parent"
android:padding= 10dp"
android:id= " @ + id/lay out"
>
Tex tView
android: lay out_ width=',wrap_content''
android:lay out_ height= " wrap_ content"
android:tex t= " Login"
/>
< EditTex t
android:id= " g+ id/tLogin"
android: lay out_width="fill_parent
android: lay out_ height= ' ' wrap_ content
/>
< Tex tView
android:lay out_ width= " wrap_ content"
android:lay out_ height= wrap_ content"
android:tex t= " Senha
/>
< EditTex t
android:id=" +id/tSenha
android: layout_width=" fill_parent"
android:lay out_ height= " wrap_ content"
android:password= " true
/>
< Button
android:id=" h-id/btLogin"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " F az er Login"
android:lay out_ gravity = " center_ horiz ontal"
/>
< /LinearLay out>
Se criarmos uma acvity com esse XML de layout, teremos um formulrio de
login, conforme a figura 10.7.
368
Google An droi d para T ablets
Figura 10.7 - Formulrio de login.
Note que o arquivo login.xml define um android :id para o LinearLay out, para
que este possa ser referenciado dentro do cdigo. Assim podemos aplicar uma
animao em toda a tela, diretamente na view que a raiz de todo o layout.
Portanto, se aplicarmos um efeito de desaparecer fade_ out, todas as views da tela
iro desaparecer juntas.
O exemplo a seguir demonstra como aplicar essa animao, mas tambm ir
chamar o mtodo Animation.setAnimationListener(AnimationListener) para monitorarmos
quando a animao vai terminar. Dessa forma, o objetivo , depois de fazer o login,
criar um efeito de fade_ out com a durao de dois segundos. Depois disso podemos
trocar de tela. Neste exemplo, para facilitar o cdigo e no termos que criar uma
nova classe, vamos apenas fechar a tela atual no final da animao.
l ExemploAn i mati on Li sten erApagarT ela
public class Ex emploAnimationListenerApagarTela ex tends Activity implements O nClick Listener,
AnimationListener {
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.login);
Button btO k = (Button) findViewById(R.id.btLogin);
btO k .setO nClick Listener(this);
}
(SOverride
public void onClick (View v) {
Cap tulo 1 0 An i maes 369
// Simular login aq ui... Se o login estiver Ok, aplicar a animao e trocar de tela
ViewGroup lay out = (ViewGroup) fintfViewByld(R.id.layout);
Animation anim = Animationlltils.loadAnimation(this, R.anim.fade_out);
// Define o listener para ser notificado sobre os eventos da animao
anim.setAnimationListener(this);
anim.setF illAfter(true);
anim.setDuration(2000);
lay out.startAnimation(anim);
}
gO verride
public void onAnimationStart(Animation animation) {
}
gO verride
public void onAnimationRepeat(Animation animation) {
}
gO verride
public void onAnimationEnd(Animation animation) {
// Agora podemos trocar de tela ou faz er q ualq uer outra coisa
finish();
}
}
Essa animao interessante e vai criar um efeito especial antes de trocar de
tela, alm de poder abrir os seus olhos para outros tipos de animaes e efeitos
especiais. A partir de agora a sua criatividade que vai fazer a diferena.
A figura 10.8 exibe o formulrio de login sendo apagado durante a validao
do login e antes de trocar de tela.
A * (?! x: * * 15h49
Anim , v ' r .'r-,'y:r,r
Oflin
ricardo
Figura 10.8 - Formulrio de login sendo apagado.
370
i
Google An droi d para T ablets
10.9 LayoutAnimationController
A classe Lay outAnimationController utilizada para animar um gerenciador de layout
ViewGroup durante a sua criao, de forma que a animao seja aplicada sucessiva
mente em todas as suas views filhas durante a criao do layout.
I magine que no exemplo da tela de login que fizemos anteriormente fosse
necessrio que, no momento de abrir a tela, view aps view recebessem um efeito
de animao, como, por exemplo, um simples fade_ in para aparecer aos poucos.
Isso diferente do que fizemos no exemplo anterior, onde aplicamos a animao
no layout raiz da tela e, consequentemente, isso afetou todas as views filhas ao
mesmo tempo. Com esse controlador de layout que veremos agora podemos aplicar
as animaes, uma de cada vez, em cada view que for adicionada ao layout da tela.
Criar esse efeito simples, e precisamos criar um arquivo XML com a animao
de layout que deve ser utilizada. Para isso crie o seguinte arquivo no seu projeto:
(Note que esse arquivo comea com a tag < lay outAnimation> e utiliza uma anima
o j existente. Neste caso escolhemos a animao fade_ in, para que cada view
aparea na tela aos poucos, uma aps a outra.
i s) /res/an i m/an i macao_layout_f ade_i n .xml
< ? x ml version= " 1.0encoding= " utf-8" ? >
< lay outAnimation x mlns:android= " http://schemas.android.com/apk/res/android' '
android:delay = " 10% "
android: animation=" anim/fade_ in"
/>
Agora que criamos esse XML com a animao a ser aplicada no layout da
tela, basta inserir uma linha de cdigo na tag < LinearLay out> , que a raiz da tela de
login, conforme demonstrado a seguir.
< LinearLay out x mlns:android= " http://schemas. android.com/apk /res/android"
android:orientation= " vertical"
android:layout_width="fill_parent"
android: lay out_ height= " fillj arent"
android:padding= ' 10dp"
android: id=" fW .d/lay out"
android :lay outAn ation= ,ganiiii/aniii!acao_layout_fade_in"
>
A figura 10.9 exibe o resultado dessa animao. Conforme o esperado, esse tipo
de animao aplicado no momento que o layout da tela est sendo criado, e as
Cap tulo 10 An i maes
371
animao aplicada em cada view, uma aps a outra, medida em que estas vo
sendo inseridas na tela.
Login
Senha
Figura 10.9 - Exemplo de animao de layout.
Note que na figura 10.9 podemos verificar que o campo de login est prati
camente visvel, enquanto o campo senha ainda est aparecendo. J o boto de
login ainda est praticamente invisvel.
Depois dessas ltimas alteraes o formulrio de login utiliza um layout de
animao fade_ in para renderizar suas views na primeira vez, e depois utiliza uma
animao fade_ out para apagar toda a tela antes de fazer a prxima tarefa.
Lembre-se que as animaes de layout so aplicadas no momento em
que o layout est sendo criado e que sero aplicadas individualmente
em cada view que foi ou ser adicionada tela.
10.10 I nterpolator
I magine que voc possui uma animao que vai mover o objeto da esquerda
para a direita com o tempo de durao de dez segundos. Podemos dizer que, se
dividirmos a distncia que ser percorrida pelo objeto por dez, o objeto ir, a
cada segundo, se mover por 10% da distncia. Ento, se a distncia total a ser
percorrida for lOOpx, o objeto ser movido lOpx por segundo. Esse o esperado
e o comportamento pdro, mas podemos tambm alterar a acelerao dessa
animao e customizar o comportamento.
372
Google An droi d para T ablets
Quem define essa taxa " rate" da animao a interface Interpolator.
Como padro, se nenhum interpolator for especificado, ser utilizada a classe
Linearlnterpolator, que faz tom que o efeito seja justamente o comentado anterior
mente, quando a animao ser consistente e ter o mesmo efeito durante todo
o tempo.
Agora vamos dizer que o objetivo seja acelerar o objeto que est se movendo. Se
pensarmos em um carro, ele comea devagar e vai ganhando acelerao, e depois
que embala vai embora. Mas lembre-se, muita calma nessa hora.
Podemos criar o mesmo efeito com a animao de um objeto e aceler-la aos
poucos. Para isso podemos utilizar a classe Acceleratelnterpolator.
Para informar qual o interpolator que deve ser utilizado basta chamar o mto
do setlnterpolator(interpolator) da classe Animation, conforme demonstrado a seguir.
Animation a =
a.setlnterpolator(new AccelerateInterpolator());
E para definir o interpolator utilizando o XML podemos utilizar o atributo
android:interpolator.
< ? x ml version= " 1.0" encoding= " utf-8?>
< translate x mlns:andnoid= " http://schemas.android.com/apk /res/android"
android:fromX Delta= " 0.0" android:toX Delta= " 0.0"
android:fromY Delta= " 0.0" android:toY Delta= " 200% "
android:fillAfter=" true"
android:duration= " 10000"
android:interpolator= " @ android:anim/acceleratej interpolator"
/>
Existem diversas classes que implementam a interface Interpolator. Fica como
exerccio que voc brinque com elas para verificar a diferena que cada uma faz
nos efeitos de cada animao. Na lista a seguir podemos verificar a explicao
das principais classes.
Mtodo Descrio
AccelerateDeceleratelnterpolator
A animao comea rpido e termina devagar, mas bem no
meio d uma acelerada.
Acceleratelnterpolator A animao comea devagar e vai acelerando.
Anticipatelnterpolator
A animao comea para trs e depois vai animando para a
frente. Esse interpolator d impresso de que o objeto vai
para trs para pegar um embalo antes de acelerar.
AnticipateO vershootlnterpolator
I dem animao Anticipatelnterpolator, mas depois de
pegar o embalo ela se empolga e acaba passando do alvo,
de forma que tem que voltar um pouco.
Cap tulo 10 An i maes 373
v' M todo---
- : /...Ti-, . Descrio
Bouncelnterpolator
Este interpolator faz com que o objeto d uns pulinhos ao
atingir o final da animao, como se uma bola estivesse
quicando no cho antes de parar.
Cy clelnterpolator
Faz com que a animao seja repetida no final por um
nmero n de vezes.
Deceleratelnterpolator A animao comea rpido e vai desacelerando.
Linearlnterpolator
Este o interpolator padro e faz com que o efeito da
animao seja constante.
O vershootlnterpolator
Esta animao se empolga e acaba passando do alvo, de
forma que tem que voltar um pouco.
10.11 ViewFlipper
I magine que voc possui duas views diferentes, digamos dois layouts de tela
distintos, e voc quer trocar de view, mas quer que tenha uma animao para dar
aquele efeito especial de encher os olhos do usurio. Uma forma simples de fazer
isso utilizando a classe ViewF lipper.
A classe ViewF lipper um gerenciador de layout especial (filho de F rameLay out) que
pode conter vrias views dentro dele, mas somente uma view exibida de cada
vez. Dessa forma, voc pode configurar uma animao de transio entre a troca
de cada view e navegar para a frente chamando o mtodo showNex tQ ou para trs
chamando o mtodo showPrevious().
Ento digamos que temos um formulrio de login e depois de fazer o login
corretamente seja necessrio exibir a mensagem Bem-vindo ao sistema! Podemos
utilizar duas views dentro de um ViewF lipper, sendo que a primeira corresponde
ao formulrio de login, e a segunda mensagem de boas-vindas, conforme este
arquivo de layout.
& /res/layout/logi n _an i mado.xml
<?x ml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android: orientation= " vertical"
android:layout_width="fill_parent"
android:layout_ height="fill_parent"
android:padding= " 10dp"
android:id= " @ + id/lay out"
374
Google An droi d para T ablets
< ViewF lipper
) . android: id="@+id/flip"
android: layout_ width= " fill_ parent"
^ android:lay out_ height= " wrap_ content"
j android:tex t= " @ string/hello"
>
<! -- View 1 -->
< LinearLay out
android:orientation= " vertical"
android:lay out_ width= " fill_ parent
android: layout_height=''fill_parent"
>
) Tex tView
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content
android:tex t= " Login"
/>
< EditTex t
) android:id= " @ + id/tLogin"
android :lay out_width="fill_parent"
1 android:lay out_ height= " wrap_ content"
/>
Tex tView
android:lay out_ width= " wrap_ content"
android: lay out _height='' wrap_content"
android:tex t= " Senha"
/>
< EditTex t
android:id="gt-id/tSenha"
android:layout_ width= "fill_ parent"
android:lay out_ height= " wrap_ content"
android:password= " true"
/>
< Button
android: id=''gtid/btLogin"
android: lay out_ width= " wrap_ content11
android: lay out_ height= wrap_ content''
android:tex t= F az er Login"
android:lay out_ gravity = center_ horiz ontal"
/>
< /LinearLay out>
< !-- View 2 >
< LinearLay out
android :orientation= " vertical
)
Cap tulo 10 An i maes
375
android: lay out_width=" fill_parent"
android:layout_ height= " fill_ parent"
>
Tex tView
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " Bem vindo ao sistema! "
android:tex tSty le= " bold
/>
< /LinearLay out>
< /ViewF lipper>
< /LinearLay out>
Note que no arquivo foi definido um ViewF lipper e dentro dele foram criados
duas views filhas, sendo que cada uma um LinearLay out.
Agora vamos criar uma activity para utilizar esse layout XML que criamos e
colocar um evento no boto de login. Quando o boto for pressionado vamos
executar o mtodo showNex t() da classe ViewF lipper para trocar para a prxima view,
iniciando a animao.
i i ExemploVi ewFli pperLogi n .java
public class Ex emploViewF lipperLogin ex tends Activity {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.login_ animado);
Button bLogin = (Button) findViewByld(R.id.btLogin);
bLogin.setO nClick Listener(new O nClick Listener() {
gO verride
public void onClick (View v) {
prox imaViewQ ;
}
});
}
private void prox imaViewQ {
ViewF lipper flip = (ViewFlipper) findViewByld(R.id.flip)j
// Animao de sada da view atual
Animation out = AnimationU tils.loadAnimation(this, android.R.anim.slide_ out_ right);
out.setDuration(2000);
// Animao de entrada da prx ima view
Animation in = Animatio'nUtils.loadAnimation(this, R.anim.fade_ in);
in.setDuration(2000);
376
Google An droi d para T ablets
// Configura a animao de entrada e sada
flip. setlnAnimation(in);
flip.setOutAnimation(out); .
// Troca para a prx ima view
flip.showNertQ ;
}
}
Durante a execuo desse exemplo, ao pressionar o boto de login o formulrio
de login comear a se movimentar para a direita para sair da tela. Enquanto
isso o texto de boas-vindas vai aparecendo devido ao efeito de fade_ in, conforme
podemos visualizar na figura 10.10.
8em vindo ao slstemal
**2: J jgl
Figura 10.10 - Animao na tela de logi?i.
10.12 View customizada com animao
Muitas vezes criamos nossa prpria subclasse de View para customizar algum
componente. E agora que sabemos utilizar a API de animao, pademos .animar
a view diretamente quando esta desenhada.
Neste exemplo vamos criar uma simples view customizada que desenha uma
imagem no centro da tela. Mas no momento de desenhar a view vamos criar
tambm uma animao de rotao, o que vai fazer a imagem girar.
i ) ExemploVi ewAn i mada
public class Ex emploViewAnimada ex tends Activity {
gO verride
protected void onCreate(Bundle savedlnstanceState) {
super. onCreate(savedlnstanceState);
setContentView(new ViewAnimada(this));
}
public class ViewAnimada ex tends View {
private Paint paint = new PaintQ ;
private int largura;
private int altura;
private Animation animacao;
public ViewAnimada(Contex t contex t) {
super(contex t);
}
public ViewAnimada(Contex t context, AttributeSet attrs) {
super(contex t, attrs);
}
gO verride
protected void onSiz eChanged(int w, int h, int oldw, int oldh) {
super.onSiz eChanged(w, h, oldw, oldh);
this.largura = w;
this.altura = h;
Log.i(" livroandroid" ,w/h: " + w + 7" + h);
}
gO verride
protected void onDraw(Canvas canvas) {
Bitmap bitmap = BitmapF actory .decodeResource(getContex t().getResources(),
R.drawable.android2);
canvas.drawBitmap(bitmap, largura / 2 - bitmap.getW idth() / 2,
altura / 2 - bitmap.getH eight() / 2, paint);
if (animacao == null) {
animacao = getAnimacaoVoltaCompleta();
startAnimation(animacao);
}
}
protected Animation getAnimacaoVoltaCompleta() {
RotateAnimation a = new RotateAnimation(0,360,
Animation.RELATIVE_ TO _ SELF , 0.5F,
Animation.RELATIVE_ TO _ SELF , 0.5F );
a.setDuration(2000);
a.setRepeatCount(10);
Capi tulo 1 0 An i maes
378
Google An droi d para T ablets
a .setRepeatM ode(Animation.INF INITE);
return a;
}
}
}
A animao ficar executando repetidamente - porque o mtodo
setRepeatM ode(Animation. INF INITE) foi utilizado.
Ao executar este exemplo podemos visualizar a imagem no centro da Cela, e
ela ficar girando sem parar, dando sempre uma volta completa no seu eixo. A
figura 10.11 exibe a imagem rotacionando durante a animao.
- A
Figura 10.11 - View customizada com animao.
10.13 I nserindo uma animao no projeto dos carros
Agora que estudamos as animaes podemos brincar com nosso projeto dos
carros e inserir alguns efeitos especiais na verso smartphone.
Para continuar o projeto dos carros vamos criar uma cpia do projeto do ltimo
captulo LivroAndroid-Cap09-Carros-ICS e renomear para LivroAndroid-Capl0-Carros-Ani.nl.,
assim podemos efetuar essa alterao em um projeto separado.
Para fazer isso vamos criar uma simples animao de fade_ in para a foto do
carro, mas que j vai dar outro acabamento para a nossa aplicao.
An i mUti l.java
Cap tulo 10 An i maes
379
public class AnimU til {
public static void animaF otoF adeIn(ImageView img) {
Animation a = new AlphaAnimation(0.0F ,1.0F );
a.setDuration(1500);
img.startAnimation(a);
}
}
Esse mtodo pode ser inserido no final da atualizao dos detalhes do carro
conforme demonstrado a seguir.
& Fragmen tDetalhesCarro.java
public class F ragmentDetalhesCarro ex tends LivroAndroidF ragment implements O nClick Listener {
private static final String TAG = "livroandroid" ;
private Carro carro;
@ 0verride
public View onCreateView(LayoutInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.layout.fragment_carro_detalhes, null);
// Abrir o site
Button btSite = (Button) view.findViewBy ld(R.id.btAbrirSite);
btSite.setO nClick Listener(this);
view.setLay outParams(new Lay outParacs(lay outParams.H ATCH _ PARENT,Lay outParams.H ATCH _ PARENT));
return view;
}
@ O verride
public void onActivity Created(Bundle savedlnstanceState) {
super.onActivity Created(savedlnstanceState);
Bundle args = getArguments();
if (args != null) {
: // Recebe o carro selecionado e atualiz a os detalhes
carro = (Carro) args.getSerializ able(Carro.K EY );
updateViewQ ;
}
}
// Atualiz a os detalhes do carro
private void updateViewQ {
View view = getView();
log.i(TAGj " Ex ibindo carro: " + carro.nome);
Tex tView tH eader = (TextView) getActivity Q .findViewBy Id(R.id.tH eader);
if (tH eader != null) {.
tH eader.setTex t(carro.nome);
} else {
380
Google An droi d para T ablets
if (! AndroidU tils.isTablet(getActivity ())) {
// Atualiz a somente se no for um tablet
ActionBarU til.setActionBarTex t(getActivity ()j carro.nome);
}
}
Tex tView tDesc = (TextView) view.findViewByld(R.id.tDesc);
tDesc. setT ex t (carro. desc);
// L a imagem do cache
final ImageView img = (ImageView) view.findViewBy ld(R.id.img);
CarrosApplication application = (CarrosApplication) getActivity ().getApplication();
DownloadlmagemU til downloader = application.getDownloadlmagemU tilQ ;
Bitmap bitmap = downloader.getBitmap(carro.urlF oto);
if (bitmap != null) {
img.setlmageBitmap(bitmap)j
img.postDelay ed(new Runnable() {
gO verride
public void run() {
AnimU til.animaF otoF adeln(img);
}
}, 0);
}
}
gO verride
public void onClick (View view) {
String uri = carro.urllnfo;
startActivity (new Intent(Intent.ACTIO N_ VIEW , U ri.parse(url)));
}
}
A figura 10.12 exibe os detalhes do carro com a animao fade_ in executando,
sendo que a transparncia da imagem ainda est bem alta antes de deixar a to
nalidade da cor totalmente opaca.
Neste captulo estudamos como criar animaes no Android e para isso uti
lizamos vrios exemplos.
No prximo captulo vamos estudar o novo framework de animaes que foi
criado a parti r do Android 3.0 e verificar o que mudou, porque foi criado, alm,
claro, de ver mais exemplos.
BMW M5 um modelo esportivo da marca
BMW. Ele um derivado da srie 5, modificado
pela motorsport, a diviso de esportivos da
marca. O modelo atual possui um motor V10 d<
507 CV, atingindo uma velocidade de 330km/h
(semlimitador, sendo que com ele a velocidade
mxima fica em 250 km/h) e chegando do
repouso aos 10Okm/h em somente 4,7s, sendo
o sedS mais rpido do mundo produzido
atualmente. O M5 recorreu a algumas
Inovaes da Frmula 1, incluindo os modos de
transmisso (manual, automtica) e electrnica
como o launch control (controlo electrnico de
arranque para as rodas traseiras no entrarem
em derrapagem. Inclui um sistema chamado
Prjwelneir nunprmjr ar* rnriHiifnr
Figura 10.12 - View customizada com animao.

1
F
G
E

-
C
A
M
P
U
S

F

R
T
A
U
E
Z
A

i
i
B
I
B
L
I
O
T
E
C
A

i
CAPTULO 1 1
Animaes com Android
3.0
No Android 3.0 foi criada a API Property Animation, que consiste em um robusto
framework de animaes que pode animar e alterar qualquer propriedade de um
objeto sobre uma linha de tempo.
Essa nova API de animao vem complementar a API de animao do Android I x,
conhecida como View Animation, a qual mais limitada e possui alguns problemas.
Este captulo visa explicar as vantagens de se utilizar a nova API Property
A nimation, e traremos todos os exemplos de animao novamente, mas desta
vez utilizando o Android 3.x.
11.10 problema com a API de animaes no Android 2.x
No Android 2.x a API de animao conhecida como View Animation e com ela
possvel criar vrios efeitos especiais, conforme estudamos no captulo anterior.
Utilizando essa API podemos criar e agrupar animaes, para exibir ou escon
der objetos, mov-los pela tela e muito mais.
O problema com o framework antigo de animao que ele altera apenas a
aparncia de uma view, mas no o seu contedo interno, isto , o estado do objeto
que est sendo alterado.
Para ficar mais claro e facilitar a explicao vamos criar um interessante exem
plo, que vai demonstrar um problema clssico com essa API.
Primeiramente vamos criar um simples formulrio com um boto na tela. O
objetivo , depois de pressionar o boto, mov-lo para baixo. Desta vez, em vez
de mover a imagem vamos mover o prprio boto.
382
Para este exemplo podemos criar um novo projeto ou utilizar o projeto existente
no captulo anterior, que o LivroAndroid-Capl0-Anim.
/ res/layout/exemplo_bug_mover_botao.xml
< ? xml version= 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android :orientation="vertical''
android: layout_width=" fill_parent"
android:lay out_ height= " fill_ parent"
android:back ground= " # eeeeee
android:padding= " 10dp"
>
< Button
android:id= " gtid/btAnimar"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content
android:tex t= " Animar"
android:lay out_ gravity = " center"
android: onClick = " onClick Animar"
/>
</LinearLay out>
A figura 11.1 exibe esse layout.
Cap tulo 11 An i maes com An droi d 3.0 3
Animar-
Figura 11.1 - Layout simples para testar a animao.
384
Google An droi d para T ablets
Para demonstrar o problema vamos criar uma animao para mover o boto
para baixo depois de pression-lo. Da mesma forma que os exemplos anteriores
de animao, vamos manter um flag na classe, para indicar se a animao j foi
realizada. Portanto, primeiramente vamos mover o boto para baixo, para depois,
na prxima vez, mov-lo para cima, na posio original.
d T ran slateAn i mBotaoBug
public class TranslateAnimBotaoBug ex tends Activity {
private boolean flag = true;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R. lay out. ex emplo_ bugj nover_ botao);
}
public void onClick Animar(View view) {
Animation a = getAnirnacaoXML ();
// Dois segundos
a.setDuration(2e00)j
// M anter o efeito no final da animao
a . setF illAfter(true);
view.startAnimation(a);
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
flag = Iflag;
}
protected Animation getAnimacaoX M L() {
int anim = flag ? R.anim.translatej ioverj ara_ baix o : R.anim.translate_ mover_ para_ cima;
Animation a = AnimationU tils.loadAnimation(this, anim);
return a;
}
}
Ao executar este exemplo e pressionar o boto para animar podemos verificar
a seguinte tela, conforme a figura 11.2.
Agora chegamos ao ponto que queramos. Para reverter a animao e mover
o boto para cima, teoricamente necessrio pressionar novamente o boto. Mas
ao pressionar o boto nada acontece!
O problema que o boto foi desenhado l embaixo, mas suas propriedades
permanecem as mesmas, de forma que o objeto no foi alterado. Ento como
se o boto continuasse l em cima, na posio original.
Figura 11.2 - Boto depois da animao.
A figura 113 exibe a posio original e final do boto. Note que, se voc pres
sionar o boto, nada vai acontecer.
Figura 11.3 - Boto depois da animao.
Mas se voc pressionar a rea onde o boto estava originalmente, representada
na figura pelo primeiro crculo, ver que a animao ser revernda, e o boto
voltar posio original. 9
Esse um dos problemas srios com a API de animao do Android 2.x, co
nhecida como View Animation.
O que aconteceu que as propriedades do boto continuaram as mesmas,
inclusive sua posio. Ele apenas foi desenhado em outro local, devido ao resul
tado da animao.
A View Animation o framework de animao do Android 2.x e
apenas altera a maneira como a view desenhada, mas deixa as
propriedades da view inalteradas, podendo causar alguns problemas.
Para continuar este captulo vamos criar um novo projeto chamado LivroAndroid-
Capll-Anim com as seguintes informaes:
Nome do projeto: LivroAndroid-Capll-Anim3
Pacote: br.livroandroid.anim3
Verso: Android 3.x ou superior
Nesse projeto vamos utilizar um menu inicial para chamar todos os exemplos
que vamos desenvolver neste captulo. A seguir podemos visualizar a classe Main
desse projeto com todas as activities que vamos criar.
Como ainda no temos essas classes, podemos deixar o cdigo comentado,
por enquanto, ou podemos criar algumas classes vazias neste momento, apenas
para o projeto compilar.
i Mai n .java
public class M ain ex tends ListActivity {
private static final String[] ops = new String[ ] {
Alpha - ValueAnimator" ,
" Alpha - O bj ectAnimator,
" Rotate - O bj ectAnimator,
" Scale - O bj ectAnimator",
" Translate - O bj ectAnimator",
" AnimatorSet" ,
" AnimationListener" ,
"Sair" };
gO verride
public void onCreate(Bundle icicle) {
super. onCreate(icide);
int lay out = android.R.lay out.simple_ list_ item_ l;
Array Adapter< String> adaptador = new Array Adapter< String> (this, lay out,ops);
386 Google An droi d para T ablets
1
Cap tulo 11 An i maes com An droi d 3.0
387
this.setListAdapter(adaptador);
}
(JOverride
protected void onListItemClick (ListView 1, View v, int position, long id) {
switch (position) {
case 0:
startActivity (new Intent(this, Ex emploValueAnimator.class));
break ;
case 1: ~
startActivity (new Intent(this, AlphaAnim.class));
break ;
case 2:
startActivity (new lntent(this, RotateAnin.class));
break ;
case 3:
startActivity (new Intent(this, ScaleAnia.class));
break ;
case 4:
startActivity (new Intent(this, TranslateAnim.class));
break ;
case 5:
startActivity (new Intent(this, Ex emploAnimatorSet.class));
break ;
case 6:
startActivity (new Intent(this, Ex emploAniuatorListenerApagarTela.class));
break ;
default:
finish();
}
}
}
Todas essas activities precisaro estar declaradas no AndroidManifest.xml, ento
podemos ir adicionando uma a uma, conforme a necessidade, ou deixar todas elas
criadas com um cdigo vazio. Escolha a sua maneira. Para efeito de comparao
podemos visualizar o arquivo AndroidManifest.xml final que vamos desenvolver.
An droi dMan i f est.xml
< ? xml version= 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.con/apk /res/android"
pck age= " br.livroandroid.anim3"
android:versionCode= " l"
android:versionName= " 1.0>
388
Google An droi d para T ablets
< uses-sdk android:minSdk Version= " ll" />
< application android:icon='' @drawable/icon" android:label= " @ string/app name"
android:theme= " @ android:sty le/Theme.H olo.Light" >
< activity android:name= " .M ain
3ndr oi d: l abel =@st ri ng/ app_name">
<intent-filter>
< action android:name= " android.intent.action.H AIN" />
< category android:name= ' ' android.intent.category .LAU NCH ER" />
</intent-filter>
< /activity >
< activity android :name=" .ExemploValueAnimator' ' />
< activity android:name= " .AlphaAnim/>
< activity android:name= " .RotateAnim" />
(activity android:name= " .ScaleAnim" />
< activity android:name=".TranslateAnini" />
< activity android:name= " .Ex emploAniraator5et" />
< activity android:name= " .Ex emploAnimatorListenerApagarTela" />
< /application>
< /manifest>
Para executar os exemplos com o novo framework Property
Animation necessrio um emulador ou aparelho com Android 3.x
ou superior.
11.2 Property Animation - A animao criada a partir do Android 3.x
A partir do Android 3.x Honeycomb foi criado um novo framework de animaes,
conhecido como Property Animation, que muito mais robusto e simples que
o anterior.
Um dos objetivos desse novo framework solucionar o problema que vimos
anteriormente, em que a animao no alterava as propriedades internas do objeto,
ela apenas mudava a forma como este era desenhado.
Agora, com a Property Animation, o conceito de animao baseado nas
propriedades do objeto que so possveis de serem alteradas, como, por exemplo,
a propriedade al pha e as posies x e y.
A Property Animation criada a partir do Android 3.x Honeycomb
soluciona o problema de animao do Android 2.x, e o seu conceito
de animao baseado nas propriedades do objeto que podemos
alterar em uma determinada linha de tempo. um conceito genrico
e poderoso.
Cap tulo 11 An i maes com An droi d 3.0
389
11.3 A classe ValueAnimator
A primeira classe que vamos estudar no novo framework a ValueAnimator, que
consiste em criar uma animao genrica e utilizar um listener para ouvir os
resultados durante a animao.
Para iniciar vamos utilizar o mesmo layout de tela que usamos no captulo
anterior, para brincar com as animaes. Esse layout define a imagem que ser
animada e dois botes para criar a animao utilizando um arquivo em XML
ou pela API.
(i /res/layout/exemplo_an i macao.xml
< ? x ml version= " 1.0 encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android:lay out_ width= match_ parent"
.android:lay out_ height= " match_ parent"
android:back ground= " # eeeeee"
>
< LinearLay out
android:orientation= horizontal"
android:lay out_width="fill_parent"
android:lay out_ height= " wrap_ content"
android:gravity = " center"
>
< Button
android: id= " @ + id/btAnimarX H L"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android :text=''Animar com X M L
android:lay out_ gravity = " center"
android:onClick = " onClick AnimarX M L"
/>
< Button
android:id= " g+ id/btAnimarAPr'
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " Animar com API"
android:lay out_ gravity = " center"
android: onClick = " onClick AnimarAPI"
/>
< /LinearLay out>
390
Google An droi d para T ablets.
< ImageView
android :id= " (5Md/img"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:src= " @ drawable/android"
android:lay out_ gravity = " center"
/>
< /LinearLay out>
Para criar o primeiro exemplo vamos inserir um arquivo genrico de animao
na pasta /res/anim da seguinte maneira:
& / r e s / a n i m / a n i m a t o r _ 1 _ p a r a _ 0 .x m l
< animator xralns:android= " http://schemas.android.com/apk /res/android"
android:duration= 1000"
android:valueF rom^ T'
android :valueTo="0"
android :valueTy pe= fioatType''
android:repeatCount= " l"
android: repeatH ode='' reverse"
/>
Podemos verificar que essa animao define que o valor inicial 1e o final
0, mas no define qual a propriedade ou o tipo da animao que ser realizada.
O conceito de animaes nesse novo framework genrico. Podemos
informar o valor inicial e final de forma abstrata, para posteriormente
vincular esses valores com alguma propriedade real.
Uma caracterstica importante que podemos verificar nesse recurso de anima
o que o tempo da animao foi definido como um segundo. Portanto, durante
esse intervalo o valor escolhido passar de 1a 0.
Feito isso, podemos recuperar essa animao e instanciar um objeto do tipo
ValueAnimator da seguinte forma:
ValueAnimator a = (ValueAnimator) AnimatorInflater.loadAnimator(this,R.anim.animator_ l_ para_ 0);
Essa configurao de animao informa o tempo e os valores, mas no define
quais as propriedades que precisam ser alteradas.
Portanto, para aplicar a animao devemos criar um listener para ficar ouvindo
o seu andamento, que durante determinado tempo vai nos enviar nmeros de 1
at 0. Esse listener pode receber os valores e aplic-los em algum objeto durante
aquele intervalo de tempo, conforme demonstrado a seguir.
Cap tulo 11 An i maes com An droi d 3.0
391
final ImageView img = ...
ValueAnimator a = (ValueAnimator) Animatorlnflater.loadAnimator(this,R.anim.animator_ l_ para_ 6)j
a.addU pdateListener(new ValueAnimator.AnimatorU pdateListenerO {
public void onAnimationU pdate(ValueAnimator animation) {
// Fica ouvindo os valores durante a animao
F loat valor = (Float) animation.getAnimatedValueQ ;
// Altera o alpha
img.setAlpha(valor); -
}
});
Note que nesse cdigo o mtodo onAnimationllpdate(animation) ser chamado vrias
vezes durante o tempo em que a animao estiver executando, e podemos recuperar
os valores que vo variar de 1 a 0, nesse caso utilizando o mtodo getAnimatedValueQ .
Feito isso, podemos fazer qualquer coisa. Neste caso estamos manualmente alte
rando a propriedade alpha da view, o que vai criar uma animao do tipo fade_ out.
Para que fique mais claro vamos estudar um exemplo completo, que pode ser
visualizado a seguir.
i ExemploValueAn i mator.java
public class Ex emploValueAnimator ex tends Activity {
private boolean visivel = true;
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super. onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View view) {
final ImageView img = (ImageView) findViewByld(R.id.img);
// Animao gen rica de 1 at 0
ValueAnimator a = (ValueAnimator) AnimatorInflater.loadAnimator(this,R.anim.animator_ l_para_0);
a.setTarget(img);
a.addllpdateListener(new ValueAnimator.AnimatorU pdateListener() {
public void onAnimationU pdate(ValueAnimator animation) {
// F ica ouvindo os valores durante a animao
F loat valor = (Float) animation.getAnimatedValueQ ;
// Altera o alpha
img.setAlpha(valor);
>
animar(a);
}
392
Google An droi d para T ablets
public void onClick AnimarAPI(View view) {
final ImageView img = (ImageView) findViewByld(R.id.img);
H Animao gen rica de 1 at 0
ValueAnimator a = ValueAnimator.ofF loat(l, 0); *
a.setTarget(img);
a.addllpdateListener(new ValueAnimator.AnimatorU pdateListener() {
public void onAnimationllpdate(ValueAnimator animation) {
// Fica ouvindo os valores durante a animao
F loat valor = (Float) animation.getAnimatedValueQ ;
// Altera o alpha
img.setAlpha(valor);
}
});
animar(a);
}
private void animar(ValueAnimator anim) {
anim.setDuration(2000);
if (visivel) {
anim.startQ ;
} else {
// Apenas reverte a animao
anim.reverse();
}
// Inverte o flag para na prxima vez utiliz ar a animao inversa
visivel = !visivel;
}
}
Ao executar esse exemplo teremos o mesmo efeito do AlphaAnimation, em que a
propriedade alpha de um objeto alterada de 1para 0, aplicando o efeito de fade_ out.
A figura 11.4 exibe o resultado desse exemplo executando no emulador do
A ndroid 3.x Honeycomb.
Como pudemos verificar, o novo framework de animao consiste em definir
um conjunto de valores que sero aplicados durante um determinado intervalo de
tempo, mas cabe ao desenvolvedor utilizar um listener para receber esses valores
e fazer a atualizao necessria em seu objeto.
A ideia legal, mas na prtica acaba sendo trabalhosa demais.
Mas espere! Esse o conceito que est por debaixo dos panos do framework,
mas ns desenvolvedores geralmente vamos utilizar a classe O bj ectAnimator, que ve
remos no prximo tpico, a qual facilita todo o trabalho de criao de animaes.
Cap tulo 11 An i maes com An droi d 3.0 393
Figura 11.4 - Exemplo da tela para fazer a animao.
11.4 A classe ObjectAnimator
At este momento j estudamos como criar animaes genricas, mas na prtica
o que queremos alterar uma propriedade, como, por exemplo, a alpha ou as
coordenadas x e y.
Para isso foi criada a classe O bj ectAnimator, que uma subclasse de ValueAnimator
e que faz tudo o que a sua classe me faz, mas tambm permite alterar uma de
terminada propriedade durante aquele tempo genrico definido pela animao.
Utilizando essa nova classe, criar uma animao do tipo fade_ out variando o
alpha de 1para 0 simples assim:
ImageView img = _
O bj ectAnimator a = O bj ectAnimator.ofF loat(img, "alpha" , lf, 0f);
anim.setDuration(2000);
a.startQ ;
O que esse mtodo est fazendo criar uma configurao de animao que vai
iniciar em 1e terminar em 0 durante dois segundos. Mas desta vez no precisamos
criar nenhum listener, pois a classe O bj ectAnimator recebe como parmetro a proprieda
de que deve ser alterada durante a animao, que neste caso a alpha. I nternamente
essa classe faz o mesmo processo manual que fizemos no exemplo anterior.
394
Google An droi d para T ablets
Existem vrias propriedades que podemos alterar. Outro exemplo seria mover
a view cem pixels para a direita, conforme demonstrado a seguir.
ImageView img = ..
O bj ectAnimator a = O bj ectAnimator.ofF loat(imgj "x", img.getX Q , 100);
anin.setDuration(1008);
a.start();
Note que anteriormente tnhamos as classes AlphaAnimation e TranslateAnimation
para implementar as mesmas coisas. Mas agora uma nica classe, chamada O b
j ectAnimator, capaz de alterar qualquer propriedade do objeto.
O conceito de animao no Android 3.x est definido como uma
transformao que aplicada durante um intervalo de tempo. Cabe
ao desenvolvedor definir apenas as propriedades que sero alteradas
durante essa execuo.
Para que a classe O bj ectAnimator altere as propriedades do objeto, temos uma
restrio: o objeto que receber a animao precisa ter os mtodos get e set das
propriedades desejadas.
Por exemplo, ao criar uma animao para a propriedade alpha podemos utilizar
o seguinte cdigo:
O bj ectAnimator a = O bj ectAnimator.ofF loat(img, "alpha", lf, 0f);
Isso significa que na classe View temos os mtodos getAlpha() e setAlpha(float).
Seguindo o mesmo princpio, sabemos que podemos alterar a propriedade x,
porque existem os mtodo getx () e setx(float).
E dessa maneira que a classe O bj ectAnimator funciona, podendo alterar qualquer
propriedade de um objeto durante a animao em um determinado intervalo de
tempo. um conceito bem flexvel.
Note que para criar as animaes estamos usando o mtodo
O bj ectAnimator.ofF loat, porque os parmetros que estamos tentando
manipular so do tipo float. Mas tambm existem os mtodos ;
O bj ectAnimator.oflnt e O bj ectAnimator.ofO bj ect, que .permitem
manipular outros tipos.
A tabela 11.1 exibe as propriedades mais comuns que podemos alterar com essa
classe para obter as tradicionais animaes de alpha, rotate, scale, translate.
Agora que conhecemos a classe O bj ectAnimator vamos replicar a maioria dos
exemplos que fizemos no captulo anterior, mas desta vez utilizando o O bj ectAni
mator diretamente pela API ou XML.
Cap tulo 11 An i maes com An droi d 3.0
Tabela 11.1 - Propriedades mais comuns utilizadas em animaes
Propriedade Descrio
alpha
Representa a transparncia da view, onde o valor 1 opaco e 0
totalmente transparente, ficando invisvel.
x e y
Essas so as tradicionais coordenadas de onde uma view est localizada
na tela. Para mover um objeto devemos alterar essas propriedades.
translationX e
translationY
Essas propriedades representam um deslocamento segundo as
coordenadas x e y. Por exemplo, se uma view ver a propriedade
translationX=-50, ela ser deslocada cinqenta pixels para a esquerda.
rotation, rotationX,
rotationY
Propriedades que controlam a rotao de uma view e recebem os
valores em graus.
scaleX e scaleY Propriedades que definem as coordenadas para escalar um objeto.
pivotX e pivotY
Propriedades que definem o eixo udlizado nas animaes de rotao e
escala, onde o padro o centro da view.
11.5 ObjectAnimator - Criando uma animao alpha
Nesse prximo exemplo vamos implementar os famosos efeitos de f ade_i n e f ade_out ,
para que a view seja exibida ou escondida, criando um efeito amigvel que pode
ser configurado para durar X milissegundos.
AlphaAn i m.java
public class AlphaAnim ex tends Activity {
private boolean visivel = true;
@ 0verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View view) {
ImageView img = (ImageView) findViewByld(R.id.img);
O bj ectAnimator a = (O bj ectAnimator) AnimatorInflater.loadAnimator(this, R.anim.fade_ out);
a.setTarget(img);
animar(a);
}
public void onClick AnimarAPI(View view) {
ImageView img = (ImageView) findViewByld(R.id.img);
O bj ectAnimator a = O bj ectAnimator.ofF loat(img, " alpha, lf, 0f);
animar(a);
}
private void animar(O bj ectAnimator anim) {
anim. setDuration(2000);
if (visivel) {
396
Google An droi d para T ablets
anim.startQ ;
} else {
// Apenas reverte a animao
anim.reverseQ ;
}
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
visivel = !visivel;
}
}
Este exemplo utiliza a classe O bj ectAnimator para criar a animao pela API,
mas tambm temos a possibilidade de utilizar um arquivo XML, que podemos
visualizar a seguir.
f i a / res / an i m/ f a de _out . xm l
< obj ectAnimator x mlns:android= " http.7/schemas.android.com/apk /res/android"
android:property Name= " alpha"
android :valueF rom= T'
android :valueTo='l0'1
android:duration= " 1000"
android :valueType="floatType" />
Ao executar este exemplo o resultado ser a animao de fade_ in e fade_ out,
como o esperado.
A classe O bj ectAnimator facilita bastante a criao da animao. Mas uma das
funcionalidades mais interessantes o mtodo reverse() disponvel na classe Ani-
mator, que faz com que uma animao execute no sentido inverso, desfazendo-a.
Uma das funcionalidades. mais interessantes no framework de
animao o mtodo reverse() disponvel na classe Animator, que faz
uma animao executar no sentido inverso, desfazendo o efeito.
Esse mtodo um espetculo, porque no precisamos criar duas animaes
distintas, uma para fazer o objeto aparecer e outra para desaparecer. Basta chamar
o mtodo reverseQ , e a animao executada no sentido contrrio. Compare essa
implementao com a desenvolvida no captulo anterior e veja a diferena.
Para criar uma animao com a classe O bj ectAnimator precisamos
informar a propriedade que vamos alterar, sendo necessrio que existam
os mtodos get e set dessa propriedade no objeto que ser animado.
Por exemplo, para animar a propriedade alpha da view podemos utilizar
esse mtodo O bj ectAnimator.ofF loat(img, "alpha", lf, 0f), e para isso os
mtodos getAlpha() e setAlpha (float) precisam existir na classe View.
Cap tulo 11 An i maes com An droi d 3.0
397
11.6 ObjectAnimator - Girando um objeto
Nesse prximo exemplo vamos replicar o exemplo que desenvolvemos anterior
mente coma classe RotateAriimation, com o objetivo de girar o objeto em um ngulo
de 360.
A seguir podemos verificar o cdigo-fonte do exemplo.
i RotateAn i m.java
public class RotateAnim ex tends Activity {
private boolean flag = true;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out. ex emplo_ animacao);
}
public void onClick AnimarX M L(View view) {
ImageView img = (ImageView) findViewByld(R.id.img);
O bj ectAnimator a = (O bj ectAnimator) AnimatorInflater.loadAnimator(this, R.anim.rotate_ 360);
a.setTarget(img);
animar(a);
}
public void onClick AnimarAPI(View view) {
ImageView img = (ImageView) findViewBy ld(R.id.img);
O bj ectAnimator a = O bj ectAnimator.ofF loat(img, " rotation, 0, 360);
animar(a);
}
private void animar(O bj ectAnimator anim) {
anim.set uration(2000);
. i f (flag) {
anim.start();
} else {
// Apenas reverte a animao
anim.reverse();
}
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
flag = Iflag;
}
}
Essa classe altera a propriedade rotation de 0oa 360. Novamente com a classe
O bj ectAnimator, tudo feito com apenas uma linha de cdigo.
O bj ectAnimator a = (O bj ectAnimator) AnimatorInflater.loadAnimaton(this, R.anim.rotate_ 360);
398
Google An droi d para T ablets
j Tambm podemos criar essa animao diretamente em XML conforme de
monstrado a seguir.
i si / r e s / a n i m / r o t a t e _ 3 6 0 . x m l
< obj ectAnimator x mlns:android= http://schemas.android.com/apk /res/android
android:property Name= " rotation"
android:valueF rom="0''
android:valueTo= " 360"
android:duration= " 1000"
android:valueType=''floatTy pe"
/>
)
11.7 ObjectAnimator Scale
^ At o momento utilizamos a classe O bj ectAnimator para alterar uma nica proprieda
de de um objeto e criara animao, mas s vezes, dependendo do caso, precisamos
alterar mais de uma propriedade.
Nesse prximo exemplo precisamos alterar duas propriedades do objeto, que
so a scaleX e a scaleY , pois vamos precisar criar o efeito de diminuir o objeto at
ele desaparecer e depois aument-lo novamente at o tamanho normal.
Para alterar mais de uma propriedade necessrio que se crie um Property Va-
luesH older com as informaes de que precisamos, neste caso as configuraes para
as propriedades scaleX e scaleY . Essa classe apenas tem o objetivo de armazenar a
informao sobre uma propriedade para ser animada.
Property ValuesH older animl = Property ValuesH older.ofF loat(" scaleX " , 1, 0);
Property ValuesH older anim2 = Property ValuesH older.ofF loat(scaleY " , 1, 0);
Depois de criar essas duas configuraes podemos agrup-las e criar um O bj ec
tAnimator utilizando o mtodo ofProperty ValuesH older(obj eto, valores...), onde podemos
informar vrias instncias de Property ValuesH older.
O bj ectAnimator a = O bj ectAnimator.ofProperty ValuesH older(img, animl, anim2);
Depois disso o objeto O bj ectAnimator resultante vai executar as duas animaes
simultneas para criar o efeito de escala tanto no eixo x quanto no y do objeto
informado.
A seguir podemos verificar o cdigo-fonte deste exempl.
)
i
Cap tulo 11 An i maes com An droi d 3.0
399
iS d ScaleAn i m.java
public class ScaleAnim ex tends Activity {
private boolean flag = true;
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super. onCreate( savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View view) {
onClick AnimarAPI(view);
}
public void onClick AnimarAPI(View view) {
ImageView img = (ImageView) findViewByld(R.id.img);
Property ValuesH older animl = Property ValuesH older.ofF loat(" scaleX " , 1, 0);
Property ValuesH older anim2 = Property ValuesH older.ofF loat(" scaleY " , 1, 0);
O bj ectAnimator a = O bj ectAnimator.ofProperty ValuesH older(img, animl, anim2);
animar(a);
>
private void animar(O bj ectAnimator anim) {
anim.setDuration(2000);
if (flag) {
anim.startQ ;
} else {
// Apenas reverte a animao
anim.reverseQ ;
}
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
flag = Iflag;
}
}
Conforme podemos verificar neste exemplo, a classe Property ValuesH older uti
lizada para armazenar as propriedades scaleX e scaleY para criar a animao final.
Note que estamos criando a animao diretamente pela API, e no via XML.
Utilizando XML podemos criar um AnimatorSet com um conjunto de animaes,
o que veremos em outro tpico mais frente.
11.8 ObjectAnimator - Movendo um objeto pela tela
Nesse prximo exemplo vamos replicar o exemplo que desenvolvemos anterior
mente com a classe TranslateAnimation, com o objetivo de mover o objeto para cima
e para baixo.
400
Google An droi d para T ablets
A T ran slateAn i m.java
public class TranslateAnim ex tends Activity {
private boolean flag = true;
private O bj ectAnimator a;
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.ex emplo_ animacao);
}
public void onClick AnimarX M L(View view) {
ImageView img = (ImageView) findViewById(R.id.img);
O bj ectAnimator a = (O bj ectAnimator) AnimatorInflater.loadAnimator(this,R.anim.mover_ baix o);
a.setTarget(img);
animar(a);
}
public void onClick AnimarAPI(View view)" {
ImageView img = (ImageView) findViewByld(R.id.img);
a = O bj ectAnimator.ofF loat(img, "y", img.getH eight() / 2, img.getH eightQ * 2);
animar(a);
}
private void animar(O bj ectAnimator anim) {
anim.setDuration(2000);
i f (flag) {
anim.startQ ;
} else {
// Apenas reverte a animao
anim.reverse();
}
// Inverte o flag para na prx ima vez utiliz ar a animao inversa
flag = Iflag;
}
}
Tambm podemos criar essa animao diretamente em XML da seguinte forma:
( ) /res/an i m/mover_bai xo.xml
< obj ectAnimator x mlns:android= " http://schemas.android.com/apk /res/android"
android:property Name= y "
android :valueFrom=''50"
android:valueTo= " 200"
android:duration= " 1000"
android:valueTy pe= " fbatTy pe
Cap tulo 11 An i maes com An droi d 3.0
401
Ao executar este exemplo, a imagem vai se mover para baixo, deslocando-se em
duas vezes o seu tamanho, conforme o mesmo exerccio que fizemos no captulo 10.
11.9 Criando um conjunto de animaes com a classe AnimatorSet
A classe AnimatorSet utilizada para criar um conjunto de animaes que podem
ser executadas em seqncia, ao mesmo tempo ou depois de um determinado
intervalo de tempo estipulado. A seguir podemos visualizar um exemplo de como
utilizar a classe AnimatorSet.
(Si ExemploAn i matorSet.java
public class Ex emploAnimatorSet ex tends Activity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R. lay out. ex emplo_ animacao);
}
public void onClick AnimarX M L(View view) {
ImageView img = (ImageView) findViewByld(R.id.img);
AnimatorSet lista = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.anim.animator_ set);
lista.setTarget(img);
animar(lista);
}
public void onClick AnimarAPI(View view) {
ImageView img = (ImageView) findViewByld(R.id.img);
float y = img.getY Q ;
O bj ectAnimator alphaAnim = O bj ectAnimator.ofF loat(img, "alpha", lf, 0f);
O bj ectAnimator translateAnim = O bj ectAnimator.ofF loat(img, "y", y, img.getH eight()*2);
// Faz emos o reverse manual aqui
alphaAnim.setRepeatCount(1);
alphaAnim. setRepeatH ode(Animation.REVERSE);
translateAnim.setRepeatCount(1);
translateAnim.setRepeatM ode(Animation.REVERSE);
AnimatorSet lista = new AnimatorSetQ ; _
lista.play Together(translateAnim, alphaAnim);
animar(lista)j
}
private void animar(AnimatorSet lista) {
lista.setDuration(2000);
lista. startQ ;
}
>
Utilizar a classe AnimatorSet pela API simples, mas tambm podemos criar o
mesmo conjunto de animaes utilizando um arquivo XML, encapsulando vrios
< obj ectAnimator> dentro de uma tag < set> .
(e i /res/an i m/an i mator_set.xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< set>
<obj ectAnimator x mlns:android=" http://schemas.android.com/apk /res/android"
android:duration= " 1600"
android:valueTo= 200"
android:valueType="floatType"
android:property Name= " x "
android:repeatCount= l"
android:repeatH ode= " reverse" />
< obj ectAnimator x mlns :android=http://schemas.android.com/apk /res/android"
android:duration= " 1000"
android:valueTo= " 400"
android: valueTy pe="floatType"
android:property Name= " y "
android:repeatCount= l"
android:repeatM ode= " reverse" />
< /set>
Note que a classe AnimatorSet no possui o mtodo reverse(). Ento
para reverter a animao seria necessrio criar manualmente outra
animao com o efeito contrrio. Mas neste exemplo estamos
executando a animao inversa habilitando o setRepeatM ode(Animation.
REVERSE) no O bj ectAnimator e tambm configuramos o atributo
android:repeatM ode= " reverse" no X M L .
Google An droi d para T ablets
I
11.10 AnimatorListener
Para monitorar o incio e o trmino de uma animao podemos utilizar a interface
AnimatorListener, e seus mtodos podem ser visualizados a seguir,
public static interface AnimatorListener {
// Indica q ue a animao foi iniciada
void onAnimationStart(Animator a) {
// Indica q ue a animao terminou
void onAnimationEnd(Animator a);
// Indica q ue a animao est repetindo
void onAnimationRepeat(Animator a);
&
// Indica q ue a animao foi cancelada
void onAnimationCancel(Animator a);
}
Note que a interface AnimatorListener do novo framework de animao
do Android 3.x muita parecida com a interface AnimationListener
que estudamos anteriormente para o Android 2.x. Mas nessa nova
interface os mtodos recebem como parmetro um Animator da nova
API, e no um Animation.
Uma das utilizaes mais freqentes desta interface se d no monitoramento
do final de uma animao para executar alguma tarefa.
Para demonstrar a sua utilizao vamos replicar o exemplo que desenvolvemos
anteriormente com a animao antiga, para criar um efeito de fade_ out, que ser
utilizado para apagar a tela atual antes de trocar para outra.
Ento mos obra e vamos criar um simples formulrio de login para utilizar
como exemplo.
i s> /res/layout/logi n .xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
android:orientation= " vertical"
android :lay out_ width= fillj arent"
android :layout_height="fill_parent"
android:id= " @ + id/lay out"
android:padding= " 20dp"
>
< Tex tView
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
android:tex t= " Login"
android:lay out_ gravity = " center_ horiz ontal"
/>
< EditTex t
android: id="(3+id/tLogin"
android:lay out_ width= " 400dp
android:layout_ height='' wrap_content"
android:lay out_ gravity = " center_ horiz ontal"
/>
< Tex tView
android:lay out_ width= wrp_ content"
android: layout_height= "wrap_ content''
Cap tulo 11 An i maes com An droi d 3.0
4 4 Google An droi d para T ablets
android:tex t= Senha"
android:lay out_ gravity = " center_ horiz ontal"
/>
< EditTex t
android:id= " @ + id/tSenha"
android:lay out_ width= " 400dp"
android:lay out_ height= " wrap_ content"
android: password= true"
android:lay out_ gravity = " center_ horiz ontal"
/>
< Button
android:id= " @ + id/btLogin"
android:lay out_ width= " wrap_ content
android:lay out_ height= " wrap_ content
android:tex t= " F az er Login"
android:lay out_ gravity = " center_ horiz ontal"
/>
< /LinearLay out>
Se criarmos uma activity com esse XML de layout, teremos um formulrio de
login, conforme a figura 11.5.
A seguir podemos verificar o cdigo-fonte completo que vai apagara tela atual,
antes de trocar de tela, caso o login seja realizado com sucesso.
2 Anim3
Stnw
.J azrlogj n^
Figiira 11.5 - Formulrio de login.
f i af arai i ^
i '
i
i Cap tulo 11 An i maes com An droi d 3.0 405
i
A ExemploAn i matorLi sten erApagarT ela.java
public class Ex emploAnimatorListenerApagarTela ex tends Activity implements O nClick Listener {
gO verride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.login);
Button btO k = (Button) findViewByld(R.id.btLogin);
btO k .setO nClick Listener(this);
}
gO verride
public void onClick (View v) {
// Simular login aqui... Se o login estiver Ok, aplicar a animao e trocar de tela
final ViewGroup lay out = (ViewGroup) findViewById(R.id.layout);
ValueAnimator anim = O bj ectAnimator.ofF loat(lay out, "alpha", lf, 0f);
anim.setDuration(2000);
anim.addListener(new AnimatorListenerAdapter() {
gO verride
public void onAnimationEnd(Animator animation) {
finish();
}
});
anim.startQ ;
}
}
Note que neste exemplo utilizamos a classe AnimatorListenerAdapter baseada no pa
dro GoF Adapter, que j implementa todos os mtodos da interface AnimatorListener,
para criar uma implementao padro para facilitar o trabalho do desenvolvedor.
Portanto, se criarmos uma subclasse de AnimatorListenerAdapter, podemos sim
plesmente sobrescrever o mtodo no qual estamos interessados, que neste caso
O onAnimationEnd(Animator animation), pois precisamos monitorar o fim da animao
para trocar ou finalizar essa tela.
Podemos visualizar o cdigo para monitorar o final da animao logo a seguir.
anim.addListener(new AnimatorListenerAdapterQ {
(SOverride
public void onAnimationEnd(Animator animation) {
finish ();
}
});
11.111nserindo uma animao no projeto dos carros
Depois de estudarmos o novo framework de animaes Property Animation dis
ponvel a partir do Android 3.x Honeycomb, vamos colocar nossos conhecimentos
em prtica e voltar a brincar com o projeto dos carros e com algum efeito especial.
A animao que vamos criar far com que a foto do carro, na tela de detalhes,
entre da esquerda para a direita, e ainda vamos utilizar o interpolator Bouncelnter-
polator para dar o efeito de que o carro est freando ao chegar na tela.
Para isso vamos atualizar a classe utilitria Animlitil que criamos anteriormente
com mais um mtodo.
d An i mUti l.java
public class AnimUtil {
/**
* Move a imagemda esquerda para direita at o centro
* Somente pode ser utilizado no Android 3.x ou superior
*/
public static void animaFotoSlideLeftRight(ImageView img) {
int x =(int) img.getX();
ObjectAnimator anim=ObjectAnimator.ofFloat(img, "x", 0, x);
anim.setlnterpolator(new BounceInterpolator());
anim.setDuration(1500);
anim.addListener(new AnimatorListenerAdapter() {
gOverride
public void onAnimationEnd (Animator animation) {
}
));
anim.start();
}
/**
* Faz umfade_in na imagem
*/
public static void animaFotoFadeIn(ImageView img) {
Animation a =newAlphaAnimation(0.0F,1.0F);
a.setDuration(1500);
img.startAnimation(a);
}
}
Note que um dos objetivos de criarmos essa classe utilitria de animaes
encapsular essa tarefa. Mas na verdade, o principal no utilizar a nova API de
animaes diretamente no fragment que exibe os detalhes do carro.
Google An droi d para T ablets
i
Cap tulo 11 An i maes com An droi d 3.0
407
Como esse fragment ser utilizado tambm por verses anteriores do cdigo,
nunca podemos referenciar diretamente numa classe em que j est carregada
uma classe ou um objeto que no existe naquela verso do Android.
Por isso, na classe F ragmentDetalhesCarro utilizamos essa classe utilitria para
decidir qual o mtodo que far a animao.
if (AndroidU tils.isAndroid_ 3_ Tablet(getActivity ())) {
AnimU til.animaF otoSlideLeftRight(img);
} else {
AnimU til.animaF otoF adeln(img);
}
A seguir podemos visualizar o cdigo-fonte atualizado da classe F ragmentDeta
lhesCarro.
(d Fragmen tDetalhesCarro.java
public class F ragmentDetalhesCarro ex tends LivroAndroidF ragment implements O nClick Listener {
private static final String TAG = " livroandroid" ;
private Carro carro;
gO verride
public View onCreateView(LayoutInflater inflater, ViewGroup Container,Bundle savedlnstanceState) {
View view = inflater.inflate(R.lay out.fragment_carro_detalhes, null);
// Abrir o site
Button btSite = (Button) view.findViewByld(R.id.btAbrirSite);
btSite.setO nClick Listener(this);
view.setLay outParams(new Lay outParams(Lay outParams,M ATCH _ PARENT,Lay outParams,H ATCH _ PARENT));
return view;
}
gO verride
public void onActivity Created(Bundle savedlnstanceState) {
super.onActivity Created(savedlnstanceState);
Bundle args = getArguments();
if (args != null) {
// Recebe o carro selecionado e atualiz a os detalhes
carro = (Carro) args.getSerializ able(Carro.K EY );
updateViewQ ;
}
}
II Atualiz a os detalhes do carro
private void updateView() {
View view = getViewQ ;
Log.i(TAG, "Ex ibindo carro: " + carro.nome);
Tex tView tH eader = (TextView) getActivity ().findViewBy Id(R.id.tH eader);
408
Google An droi d para T ablets
if (tH eader != null) {
tH eader.setTex t(carro.nome);
} else {
if (!AndroidU tils.isTablet(getActivity ())) { *
// Somente atualiza se no for um tablet
ActionBarU til.setActionBarTex t(getActivity (), carro.nome);
}
}
Tex tView tDesc = (Tex tView) view.findViewById(R.id.tDesc);
tDesc.setTex t(carro.desc);
// l a imagem do cache
final ImageView img = (ImageView) view.findViewById(R.id.img);
CarrosApplication application = (CarrosApplication) getActivity ().getApplication();
DownloadlmagemU til downloader = application.getDownloadImagemU til();
Bitmap bitmap = downloader.getBitmap(carro.urlF oto);
if (bitmap != null) {
img.setlmageBitmap(bitmap);
img.postDelay ed(new RunnableQ {
(SOverride
public void run() {
// No podemos invocar diretamente a nova API de animaes do Android 3.x
// Pode dar erro em tempo de ex ecuo em verses anteriores do Android
if (AndroidU tils.isAndroid_ 3_ Tablet(getActivity ())) {
AnimU til.animaF otoSlideLeftRight(img);
} else {
AnimU til.animaF otoF adeln(img);
}
}
}, 0 );
}
}
gO verride
public void onClick (View view) {
String uri = carro.urllnfo; 5
startActivity (new Intent(Intent.ACTIO N_ VIEW , U ri.parse(url)));
}
}
A figura 11.6 exibe o resultado da animao, onde podemos visualizar o carro
movendo-se da esquerda para a direita. A posio final do carro ser o centro da
tela, e a figura exibe o incio, da animao.
Cap tulo 11 An i maes com An droi d 3.0 409
I pot l ev-to i* nvei oddtde mi u n w de 260 mpi e t em i m
qpoar de 0-62 ml IhK em 2,78 seguido. Veloadade o que t orru o Ul nmxe Aero f erert e dm out oi
Figura 11.6 -Animao no fragment de detalhes do carro. ____
^ ^
J inserimos algumas animaes no projeto. Agora o resto com voc! j j |
No prximo captulo vamos verificar como integrar o Google Analytics e o j ^
Google Mobile Ads ao projeto.
>is: c
: >-) o
* U - LU
At logo.
a S
* - 02
j & c
I <
j ?
U.J
'J
! - 1
CAP TULO 1 2
Google Analytics e
Google Mobile Ads
Neste captulo vamos utilizar o SDK do Google Analytics para monitorar as
funcionalidades de nossa aplicao que esto sendo mais utilizadas, para pos
teriormente tirar importantes mtricas com os relatrios disponveis no site do
Google Analytics.
Tambm vamos utilizar o SDK do Google Mobile Ads e aprender a adicionar
anncios na aplicao, os quais so disponibilizados gratuitamente pelo Google,
e ainda temos a oportunidade de rentabilizar a aplicao com esse recurso.
12.1 Introduo ao Google Analytics
Como fazemos para medir a quantidade de usurios que esto utilizando a apli
cao? Como fazemos para descobrir quais as partes da aplicao que so mais
acessadas?
Essas so perguntas que o Google Analytics pode nos ajudar a responder, e ao
instalar o SDK no nosso projeto poderemos monitorar partes da nossa aplicao
para extrair importantes mtricas para anlise futura.
O Google Analytics a clssica ferramenta de gesto para websites onde di
versos relatrios e mtricas so gerados para medir a eficincia do marketing de
um website. E agora, para alegria geral dos desenvolvedores mobile, todos esses
recursos podem ser explorados tambm nos sistemas operacionais mveis, como
o Android.
O Google Analytics uma ferramenta grande e completa, e nosso objetivo
aqui apenas mostrar a sua integrao com o Android. Para maiores detalhes
consulte a documentao oficial.
410
12.2 Criando uma conta no Google Analytics
Para comear a brincadeira vamos criar uma conta no Google Analytics acessando
esta pgina: http://www.google.com/analytics/.
Para criar a conta precisamos informar o endereo de um site, que neste caso
pode ser um nome fictcio, mas algo que represente alguma informao concreta.
Para este livro o cadastro foi feito em nome do site exemplo.livroandroid.com.br,
conforme podemos visualizar na figura 12.1.
Google An alyti cs
Cap tulo 12 Google An alyti cs e Google Mobi le Ads 411
Googl* Anafyttcs: tacri to pira urra nova conti
Wuirtu^iirf mnoa*tf Jtomaj+i a* muu*MM* oouiufci>Miae*Mf KnYikinirrrtrto
h ii &I f l L 03 t u tf it M U j i mcnttrii <xtihj o tv* i itt utM *m tiu* iHj!*im de ooj * AAih4 2*ri Konvinh! mus Otu
CC<rlpjrr m cctl
W . dftia* | rijiJ (tntntfo frcqnq1gm t* ijj* cmj
dc9*s J*r*iY<awiMttiMioaeemtu
l>AailnifiadftMhfttt: |Bm<
Ftiwhw-ncc j iij TD) 00) 3toPuo
C M I g t fi k i e t d*bM
S w o r t rrtl wr* rrct4 k m j p M gix k * i q *3$ wv dadoi to Oocq*f Anh1
EOlJ f OKn IT- j * m* a
10535^1
Figura 12.1 - Criando uma conta no Analytics.
Depois disso s seguir o passo a passo e no final teremos uma tela com um
script. Esse script aquele clssico cdigo que precisamos colocar nas pginas
dos websites para criar os relatrios, mas neste caso somente precisamos conhecer
o cdigo de acesso ao Analytics, que est dentro do script.
Se repararmos na figura 12.2, podemos visualizar o cdigo U A-26233730-l dentro
do script, o qual a nossa chave de acesso ao Analytics. Lembre-se que para cada
aplicao cadastrada no Analytics ser gerado um cdigo diferente.
_ gaq . push([ ' _ setAccount', 'U A-26233730-1']);
Depois de concludo o cadastro podemos visualizar os relatrios para nossa
conta, conforme a figura 123.
Neste caso, na primeira coluna podemos ver o endereo do site fictcio h t t p : / /
exemplo.livroandroid.com.br e ao lado novamente o cdigo U A-26233730-i de acesso,
do qual vamos precisar para enviar os dados para o Analytics.
412
Google An droi d para T ablets
Google Analytics
Gootft Arajytjca: I nrtrugfoi 6* KompThamwite
p* tHt i a m fc Wwmfcj n a* etnuu * axm o taftsui> utufcia hatxxi*
O 0 que voc et ewnpwihando? Q col* st* cidigo m MU slts
mc j a p i p f U qu out * r ::pM z a p * * 71**
meta. Sj Om*#i
Pemxv
f LVn dmne djmvirot tuWcmhoi
UJfnr^ci <Jf rMl tuMTOf
r * Ou* rc:orronr>>r (r Tp y i r ^ i 6m>* Vf / i & s
<*orip yp*-*M>c/5v*cnpc">
v w j ^T O M m
pu*b t C > g f v l i v4J) i "
(tuscciDnO <
vi - 49>MB.rElMK('crlpc>j o
p.rc (bpn* -- dM WK .lM K tsi^rvt
w pw;uMu,9eEltMK>V7T9)fM(9rip
>) oi.
41 *; \ "r?- a-i :i^'^\!7.v'-r.n7.-;^gt,?rr:?a:ye?rryi? vA~:-.-ri|~ -.
O p c l o f f a J : t n m a i p e r - e & u i
^-v- RttctTUKHUiVi}! Qjiotfr rao n t b i e * a nem Oatiiapa a Korrinrkimtra j j mn nw b*3 rtio p * p*rw
p*f>n)*lcijnail 0* MI *t* wr>n7*j Cttkp? tf* *Cvrmhjrn*rt5 mjn*5 o*dp? t r t qot mmwi s t rmOtbw
rSM^6bcMf I
Figura 12.2 - Conta criada no Analytics.
Vlil* graJ x*apl.Vvran4rW.era^r ai co-mj
04/05/2011 - 05/10/2011
Ccru*;kirt* 07t1. IB9*?Q11
B3fon
UjjjjjjjjU
jft! SW*
_>r. J*
MtyU* - ?* 23371
rrzzir r ' ' -----r T^crr*
HO toar | tc^j r
..[.a .], i.rjjTi;
Mkisnit ptvfli ri Cwnd* 4w <eiwlrtee
ereTTW *<t.w>* vnattiA m>vc" U n i u n k n tfvoaartas 1
a o i >: o i o i s e mw i UcdMiMM(viiiufa>!i Smi k m
Cmnd*4( <to
Um i MAdcI
<Hntrt etijn ;-ri m**
el i l fXt>375*n
Figura 12.3 - Relatrio vazio do Analytics.
Por enquanto os relatrios no exibem nenhuma informao, pois ainda no
chegamos a utilizar a aplicao. Portanto, vamos configurar o Analytics no projeto
dos carros e voltaremos a este relatrio para visualizar o resultado.
Cap tulo 12 Google An alyti cs e Google Mobi le Ads
413
Uma informao importante que precisamos saber que demora um dia para
as informaes serem disponibilizadas. Portanto, tenha pacincia, pois ativando
o Analytics na aplicao hoje, s ser possvel verificar os relatrios amanh.
12.3 Fazendo o download do SDK do Google Analytics
A ideia continuar o projeto dos carros, ento, antes de comearmos a brincadeira
vamos configurar o projeto.
Para isso podemos criar uma cpia do projeto atual e renomear para LivroAn-
droid-Capl2-Carros-Ads, para iniciar as prximas customizaes.
Com o projeto criado, vamos baixar o SDK do Analy tics no endereo http-.Hcode.
google.com/mobile/analytics/docs/android/.
Depois de fazer o download do SDK basta descompactar o arquivo, pois ele
consiste em um simples projeto de exemplo, um arquivo HbGoogleAnalytics.jar, o
qual precisamos adicionar no classpath do nosso projeto.
A maneira recomendada de fazer isso criar uma pasta lib no projeto e copiar
o arquivo HbGoogleAnalytics.jar para essa pasta. Depois clique com o boto direito
no arquivo e selecione o menu Bui ld Path >Add T o Bui ld Path, e pronto. Outra manei ra
de fazer isso clicar com o boto direito no projeto, entrar em propriedades e na
opo Java Bui ld Path >Li brari es adicionar esse j ar manualmente.
Feito isso, podemos utilizar a API do Analytics no cdigo e mapear todos os
lugares do aplicativo em que desejamos monitorar os eventos.
Utilizar a API simples, conforme podemos visualizar no trecho de cdigo a seguir.
// Cria a instncia de GoogleAnaly ticsTrack er
GoogleAnaly ticsTrack er track er = GoogleAnaly ticsTrack er.getlnstanceQ ;
// Inicia uma sesso para nossa chave
track er.startNewSession(" U A-26233730-r, context);
// Contabiliz a o acesso de um pageview
track er.track PageViewfVcarros" );
Primeiramente precisamos obter uma instncia de GoogleAnaly ticsTrack er e iniciar
uma nova sesso, e para isso precisamos informar nossa chave de acesso U A-26233730-
1, que obtivemos na administrao do site.
Depois para criar os pageviews basta chamar esse simples mtodo informando o
caminho simblico para o qual desejamos criar alguma mtrica, como, por exemplo:
track er.track PageView(" /carros" );
414
Google An droi d para T ablets
Posteriormente, no website do Analytics poderemos visualizar um relatrio
com a quantidade de pageviews gerados para vrias partes da nossa aplicao.
E recomendado que o mtodo track er.startNewSession(" U A-26233730-r, contex t)
seja chamado uma nica vez durante a aplicao tambm que cada pageview
feito com o mtodo track er.track PageView(' ' /carros" ) seja disparado em uma thread
separada, para no travar a interface da tela.
Para nos auxiliar nessa tarefa vamos utilizar uma classe utilitria chamada
Analy ticslltils, disponvel no projeto open-sotirce http://c0de.g00gle.c0m/p/i0sched/.
Fiz pequenas modificaes nessa classe para facilitar o exemplo, mas deixei os
comentrios ainda em ingls conforme o cdigo-fonte original.
Note que dentro da classe declarada uma constante com a chave de acesso
ao Analytics.
private static final String U ACO DE = " U A-26233730-1" ;
& An alyti csUti ls.java
public class Analy ticsU tils {
private static final String TAG = " Analy ticsU tils;
private static final boolean LO GJ DN = true;
private static final boolean ANALY TICSJ NABLED = true;
private GoogleAnaly ticsTrack er mTracker;
private Contex t mApplicationContex t;
/**
* Cdigo para o Google Analy tics do LivroAndroid
*/
private static final String U ACO DE = " U A-26233730-1" ;
private static Analy ticsU tils slnstance;
/**
* Returns the global { @ link Analy ticsU tils} singleton obj ect, creating one if necessary.
*/
public static Analy ticsU tils getInstance(Contex t contex t) {
if (! ANALY TICSJ NABLED) {
return sEmpty Analy ticsU tils;
}
if (slnstance == null) {
if (contex t null) {
return sEmpty Analy ticsU tils;
}
slnstance = new Analy ticsU tils(contex t);
}
Cap tulo 12 Google An alyti cs e Google Mobi le Ads
415
return slnstance;
}
private Analy ticsU tils(Contex t contex t) {
if (contex t == null) {
// This should only occur for the empty Analy tics utils object.
return;
}
mApplicationContex t = contex t.getApplicationContex t();
mTrack er = GoogleAnaly ticsTrack er.getInstance();
// J nfortunately this needs to be sy nchronous.
mTrack er.startNewSession(U ACO DE, 309, mApplicationContex t);
if (L0G_0N) {
Log.d(TAG, " Initializ ing Analy tics" );
}
}
public void trackEvent(final String category , final String action,
final String labei, final int value) {
// We wrap the call in an Asy ncTask since the Google Analy tics library
// writes to disk on its calling thread.
new Asy ncTask < Void, Void, Void> () {
gO verride
protected Void doInBack ground(Void... voids) {
try {
mTrack er.track Event(category , action, labei, value);
if (L0G_0N) {
Log.d(TAG, " livroandroid Analy tics track Event: + category + " / "
+ action + " / " + labei + " / " + value);
}
} catch (Ex ception e) {
// W e don't want to crash if there' s an Analy tics library exception.
if (L0G_0N) {
Log.w(TAG, " livroandroid Analy tics track Event error: " + category
+ " / " + action + " / " + labei + " / + value, e);
}
}
return null;
}
}.execute();
}
public void track PageView(final String path) {
// W e wrap the call in an Asy ncTask since the Google Analy tics library
// writes to disk on its calling thread.
new Asy ncTask < Void, Void, Void> () {
gO verride

protected Void doInBack ground(Void... voids) {


try {
mTrack er.track PageView(path);
if (L0G.0N) {
Log.d(TAGj " livroandroid Analy tics track PageView: "+ path);
}
} catch (Ex ception e) {
// We don' t want to crash if there' s an Analy tics library
// ex ception.
if (LOG_ ON) {
Log.w(TAG," livroandroid Analy tics track PageView error: "+ path, e);
}
}
return null;
}
} .ex ecute();
}
/**
* Empty instance for use when Analy tics is disabled or there was no Contex t available.
*/
private static Analy ticsU tils sEmpty Analy ticsU tils = new Analy ticsU tils(null) {
gO verride
public void track Event(String category, String action, String labei,int value) {
}
@ O verride
public void track PageView(String path) {
}
};
}
Outra configurao necessria no projeto adicionar as permisses de internet
e verificar o estado da rede no And.roidMa.mfest.xml. Mas no caso do projeto dos
carros essas permisses j existem.
< uses-permission android:name= " android.permission.INTERNET" /)
< uses-permission android:name= android.permission.ACCESS_ NETW 0RK _ STATE7>
Depois de criar essa classe no projeto vamos adicionar alguns pageviews na
aplicao no prximo tpico.
12.4 Criando os pageviews
O prximo passo adicionar os eventos do Analytics em todos os pontos que
desejamos monitorar. I sso fica ao seu critrio.
416 Google An droi d para T ablets
Cap tulo 12 Google An alyti cs e Google Mobi le Ads 417
No aplicativo dos carros podemos monitorar os eventos sempre que o usurio
selecionar alguma das opes do dashboard. Por exemplo, ao selecionar a opo
Esportivos, podemos chamar o seguinte mtodo para contabilizar esse acesso
no Analytics.
Analy ticsU tils.getInstance(this).track Event(Dashboard" , " Click ", " Esportivos" , 8);
Nesse mtodo o primeiro parmetro a categoria, o segundo o evento, e o
terceiro o labei que identifica esse evento.
Isso pode ser feito para todos os cliques da aplicao, como, por exemplo, ao
selecionar as Tabs na verso que utiliza a ActionBar.
O cdigo-fonte da classe F ragmentDashboard alterada para contabilizar os eventos
com o Analytics pode ser visualizada a seguir. Parte do cdigo est comentado,
para facilitar o entendimento.
D Fragmen tDashboard.java
public class F ragmentDashboard ex tends LivroAndroidF ragment implements O nClick Listener {
public View onCreateView(android.view.Lay outInflater inflater,android.view.ViewGroup Container,
Bundle savedlnstanceState) {
return view;
}
@ 0verride
public void onClick (View v) {
Contex t contex t = getActivity ();
boolean redeOk = AndroidU tils.isNetwork Available(contex t);
if (redeO k) {
// Verifica se tablet cora Android 3.x ou superior
boolean android3 = AndroidU tils.isAndroid_ 3();
// Cria uma Intent com telas diferentes para smartphone e tablet
Intent intent = new
Intent(contex t,android3 ? TelaListaCarrosTablet.class : TelaListaCarros.class);
if (v == btEsportivos) {
Analy ticsU tils.getInstance(getActivity ()).track Event(" Dashboard" , "Click",
"Esportivos", 0);
intent.putEx tra(Carro.TIPO , Carro.TIP0_ ESP0RTIV0S);
startActivity (intent); .
} else if (v == btClassicos) {
Analy ticsU tils.getInstance(getActivity ()).track Event(" Dashboard" , Click",
"Clssicos", 0);
intfnt.p tEx tra(Carro.TIPO , Carro.TIPO _ CLASSICO );
418 Google An droi d para T ablets
startActivity (intent);
} else if (v == btLuxo) {
Analy ticsU tils.getInstance(getActivity()).trackEvent(" Dashboard", "Click", " Lux o, 0);
intent.putEx tra(Carro.TIPO , Carro.TIPO LU X O );
startActivity (intent);
} else if (v == btSobre) {
Analy ticsU tils.getInstance(getActivity()).trackEvent(" Dashboard", "Click ", "Sobre", 0);
// No tablet 3.x .pode ser que o F ragmentSobre j estej a na tela
View lay outF ragSobre = getActivity ().findViewBy Id(R.id.lay outF ragSobre);
if (lay outF ragSobre == null) {
startActivity (new Intent(contex t, TelaSobre.class));
}
}
} else {
AndroidU tils.alertDialog(contex t, R.string.erro_ conex ao_ indisponivel);
}
}
}
Tambm podemos monitorar os pageviews que servem para contabilizar as
reas acessadas da aplicao. Por exemplo, ao entrar na tela que lista os carros,
podemos executar o seguinte mtodo:
Analy ticsU tils.getInstance(this).track PageView(' 71ista_ carros" );
Esse cdigo pode ser inserido no final do mtodo onCreate(bundle) de uma activity
ou onCreateView(inflater, viewGroup, bundle) de um fragment.
Por exemplo, no projeto dos carros a verso smartphone utiliza a activity Te-
laListaCarros para exibir a lista, e podemos adicionar um pequeno cdigo para
contabilizar esse pageview.
Analy ticsU tils.getInstance(this).track PageView(" /lista_ carros" );
Mas agora com voc! Fique vontade para monitorar sua aplicao e adi
cionar os eventos nos lugares que achar apropriado.
No prximo tpico vamos visualizar os relatrios gerados pelo projeto dos
carros no site do Google Analytics.
12.5 Visualizando os relatrios
Chegou o momento de visualizar os relatrios que foram gerados pela nossa
aplicao, e para isso vamos entrar na administrao do site do Google Analytics.
Cap tulo 12 Google An alyti cs e Google Mobi le Ads
419
Lembre-se que os relatrios so sempre gerados com um dia de
atraso. I
Apenas para demonstrar um dos relatrios do Analytics, eu brinquei um
pouco aqui com essa verso do aplicativo dos carros, navegando tanto na verso
smartphone quanto na tablet.
A figura 12.4 demonstra o resultado da brincadeira e exibe um relatrio que
demonstra a quantidade de acessos nas partes monitoradas do aplicativo.
Viso geral de contedo
| />/* VluUtfedepflin ^|
10/09/2011- 10/10/2011
Critica por
As pgi nas desse si t e f oram vi sual i zadas 99 vezes no t otal
_________ I 99 Visuall2aef de piglni
_J 15 Vi* ualiaa5es nica*
_ 0,00%Taxad* reJelSes
Cont edo pri nci pal
Ar]j>KMl.Cari 9>
etatei.cwci
flrag_eBf
visual car j elatno compielo
5451
17.17%
Anlisedenavegao
^ Resuma da navegao
i uiarampan crrgar aseueontuK
Otimizaodapginadedestino
Cg Origart* d i entrada
Prrcc-M i j rt w pa
^ Pal Jvras-dvJv* do ontrada
i mxw uew.i-is-CTCT^por
Padresdediques
Figura 12.4 - Relatrio do Analytics.
O Analytics sempre foi uma ferramenta essencial para a anlise das campanhas
e acessos em websites, e agora tambm est disponvel para as plataformas mveis.
Outra importante ferramenta disponvel para os aplicativos mveis so os
anncios de publicidade disponibilizados gratuitamente pelo Google e que po
dem ser inseridos nas telas das aplicaes. De quebra, dependendo da populari-
dadmado aplicativo esses anncios podem ainda rentabilizar um bom dinheiro.
No prximo tpico vamos estudar o Google Mobile Ads e adicionar anncios
no aplicativo dos carros.
420
Google An droi d para T ablets
12.6 I ntroduo ao Google Mobile Ads - AdMobs
No Google I /O 2010 foi apresentada a plataforma AdMbs, e todos os conceitos
foram explicados durante uma palestra de quarenta minutos. Essa palestra pode
ser encontrada no Y ouTube ou no site do Google I/O, basta procurar pelo ttulo
Google I /O 2010Analyzing and monetizing your mobile apps. E extremamen
te recomendado assistir ao vdeo, pois so exibidos dados muito interessantes,
grficos e nmeros.
Como se no bastasse, durante o Google I /O 2011 novamente uma grande
palestra foi feita sobre o tema, com o ttulo Dort just build a mobile app. Build
a business. E novamente deixo recomendado que se assista ao vdeo.
Durante a palestra foram explicadas as diversas formas de rentabilizar a sua
aplicao, conforme a figura 125, que foi extrada de um dos slides da apresentao.

9
I .
1
Paid downloads Freemium
| In-app purchase Advertsing
User pays a fee
User pays for
^User purchases User clicks
to download additional
goods within on ads
functionality
jthe app
Figura 125 -Apresentao Google I/O 2011.
Basicamente, existem quatro maneiras para rentabilizar uma aplicao. A tabela
12.1 explica cada uma delas.
Tabela 121 - Maneiras de rentabilizar uma aplicao
Parmetro Descrio
Paid downloads
Este o tradicional mtodo em que o usurio paga para efetuar o
download da aplicao.
Premium
Aqui os usurios podem instalar a aplicao gratuitamente, mas
pagam para adquirir mais funcionalidades.
In-app purchase
Esta outra maneira de disponibilizar a aplicao de forma
gratuita, mas cobrar por compras dentro do aplicativo utilizando
a plataforma integrada do Android. No caso de um jogo poderiam
ser compradas novas fases, poderes especiais, e no caso de uma
aplicao poderiam ser adquiridos novos mdulos e recursos.
Advertsing
Neste mtodo a aplicao disponibilizada de forma gratuita, mas
anncios de publicidade so adicionados na aplicao.
1
Cap tulo 12 Google An alyti cs e Google Mobi le Ads 421
Neste tpico estamos interessados em disponibilizar os anncios, ou Ads
como so conhecidos, portanto vamos utilizar a quarta maneira Advertsing
de se rentabilizar a aplicao.
Para isso vamos continuar nosso projeto dos carros e adicionar alguns ann
cios, conforme podemos verificar na figura 12.6.
Clssicos Luxo
Esportivos Sobre
Celulares e Smartphones ... . r
Oymm eWl r* t om di W9i o* ; f
OVfUV
w woAndt M^Tews direi to* TCeivado*
UmborgiuaiAveniaoor /
Chevrolet Corvette 206 >
BMWM5 > _
Renault Mejane RS Trophy >
Maseratl Grancabrio Sport >
>
MERCEOES-MNZ C63 AMG >
Tablets no Maquine Luiu
Tle And'cd m j tt 12i S*Ju*p Ettst f
SpfCVf ?Map.vw lwa,.
MCLAREN UP4-12C
Figura 12.6 -Anncios no aplicativo dos carros.
O funcionamento simples. Basta criarmos uma view do anncio e inseri-la nas
telas que desejarmos. Essa view tem o formato de um banner, e a regra simples. A
cada vez que algum usurio clicar sobre os anncios voc ganhar R$. O quanto
isso vai render vai depender de vrios fatores, mas principalmente da popularidade
de seu aplicativo. Para maiores informaes consulte a documentao oficial.
12.7 Google Mobile Ads
O Google, em sua estratgia de explorar o marketing e a publicidade nas redes
mveis dos smartphones e tablets, adquiriu a empresa Mobile AdMob. Com isso
temos hoje uma consolidada plataforma de anncios disponvel para desenvolver
aplicaes para Android. Essa plataforma o Google Mobile Ads, e a documen
tao oficial pode ser encontrada no link http://www.google.com/ads/mobile/.
A figura 12.7 mostra o site do Google Mobile Ads com uma descrio sobre
o servio.
422
Google An droi d para(T ablets
Google Mobile Ads
i Over / i ew v . j ^ e r t i s e r s Publishers . Mobi l e i nsi ght s *
OverVi ew App devel opers Web publi shers Ti psandt ool s Case studi es
App developers
Joi n thousands of app developers worldwi de l o grow your.mobi l e app busi ness wi t h AdMob by
Googl e.
Earn revenue across pl at f orms wi th AdMob's mobil e-speci f i c f ormais and the reach of
Googl es adverti ser netvvork.
Promot e your appswi t h a pai d ad campai gn on t he AdMob net wotk or for free on your
own properti es wi t h AdMob House Ads.
Measure perf ormance wi th reaMi me reporti ng that helps you evaluate perf ormance
across ad pl acement s, OS platf orms and geography.
Figura 12.7 - Site do Google Mobile Ads.
Na prtica os servios de publicidade esto sendo fornecidos pela empresa
AdMobs, que foi adquirida pelo Google. Nessa pgina podemos verificar um
boto azul Getstarted wi th AdMob, que ao ser pressionado vai lhe redirecionar para
o site da AdMobs para que voc faa o seu cadastro.
Realizar o cadastro simples. Basta preencher um formulrio. Se voc j possui
uma conta do Google, pode inclusive vincular essa sua conta no AdMobs, o que
facilita o cadastro.
Ento vamos l! Clique nesse boto para entrar no site da AdMobs ou v direto
fonte e faa o seu cadastro. .
http://www.admob.com/
12.8 Registrando a aplicao no AdMobs
Depois de entrar no site do AdMobs e criar uma conta podemos visualizar a
seguinte tela, conforme a figura 12.8:
No momento de criar uma conta no AdMobs voc dever informar
uma conta bancria vlida para pagamento ou uma conta no PayPal.
Se necessrio, verifique mais informaes na ajuda do cadastro. \
Cap tulo 12 Google An alyti cs e Google Mobi le Ads
& Apps Add Site/App
Add Si t e/ App
Sttelnfo ft.: '' ? -Get Site
Select a site or app type
Wridcxvs Phcne
7 iw
Figura 12.8 - Administrao do AdMobs.
Nessa pgina possvel selecionar a opo An droi d App e preencher o formulrio
com os dados da aplicao.
E importante preencher o campo An droi d Package com a notao do Android
Market, que https://mark et.android.com/details? id= { pacote da aplicao aq ui} .
No caso deste livro, o projeto dos carros ter a seguinte nomenclatura para o
pacote:
https://market.android.com/details?id=br.livroandroid.carros
A figura 12.9 exibe a forma como eu preenchi a aplicao para o cadastro de
carros ao criar este exemplo.
424
Google An droi d para T ablets
Add Si t e/ App
Sefect a site or app type
l l f g i i . ,

: - V
m
l i
M S M
Android App Pad App PhoneApp Smartphoo
Web
Windows Phone
7 App
App name: l vroAndroi d - Caros*
Andrcnd Packae
Category : 1 Ref erenca
Jmf t ri t et / / det ail s?idbrJwroandroid.corros
Eg: m*rlnl://dt*ils id*pek*9nrot
------B .
App description:
E x e m p l o d o L i v r o n d r o i d p a r a t a b l e t s c o m
A n d r o i d 3 . 0 . P o s s u i u m a c e l a c o m d a s h b o a r d ,
e a l i s t o d e c a r r o s e x b i d a c o m a s n o v a s
A P I s d e F r a g m e n t s e A c t i o n B a r .
Figura 12.9 - Criando a aplicao tio AdMobs.
Depois de cadastrar a aplicao a tela ser redirecionada para outra, onde
podemos baixar o SDK do AdMobs para Android, conforme a figura 12.10.0 SDK
consiste em um simples arquivo .jar que devemos incluir no classpath do nosso
projeto. Faa o download desse arquivo, que posteriormente ser utilizado para
configurar o projeto.
Depois de concludo o cadastro, na prxima vez que acessarmos o menu Si tes/
Apps ser possvel visualizar um relatrio resumido com os lucros do projeto. Assim
esperamos. Mas neste momento, conforme a figura 12.11, ainda est tudo zerado.
Na listagem temos nosso aplicativo Livro Android Carros onde na coluna
tipo podemos ver um bonequinho verde do Android, e ao lado, na coluna Status,
uma bolinha vermelha indicando que o servidor ainda no recebeu nenhuma
requisio do aplicativo.
Cap tulo 12 Google An alyti cs e Google Mobi le Ads 425
Add New Sfte/App
: Add New Sites / Apps
Install Code - LivroAndroid - Carros
The AdMob AnctoRJ SDK includes:
1. READM Get started with AdMob Android ads!
2. AdMob fSe Requred for pubfchng ads. Fofow the docunentotion in javadoc/hdex.html and f r op tf
3. J avadoc API Documentation For the AdMob Android SDK.
Se* develope^s guide, exempteSj end FAQ t Google Code. o
Figura 12.10 - Download do SDK do AdMobs.
Sites & Apps
'Z CongreUf at mi Yai havc succKf u/ creat edyw titel
To artyse yar st t srt t rrj , ck>. on Manage Settirijs.
$0.00
Pendng Eamngs
$0.00
AdBal mce: $0.00 TrefHeFi nds
Ye* t rdy
l J 7 Der?
SQ.00<&%]
$0.00 (0%)
SOOOpt)
*0.00(0)
L W311-1CL0700ao GMT
E!AMa.)Ag, i
w
Set table date range:
g j 2011/ 09/ 07 -2011/ 10/ 07
1 ; Nane ' 1 ' - . - Typ stft? ' Reverue Reojests: ; . eCPM FRate RPM 1
UtToAndad Carro* $0.00 0 $0.00 0.00% $0.00
S m r q l -2 ei 2
J <hevftji Q | *.*'* -|
Account Total; $0.00 o $o.oo 0.00% $0.00
Figura 12.11 - Projeto dos carros configurado tio AdMobs.
Depois de criada a configurao para o projeto br.livroandroid.carros, clique no
item Livro Android - Carros na lista e entre nas configuraes.
426
Google An droi d para T ablets
Feito isso, ser exibida uma tela, conforme a figura 12.12, onde podemos ver
um texto Publisher ID: ai4e8efa5b8f52f. Essa chave que identifica a nossa aplicao
no servidor do AdMobs. Para criar o anncio vamos precisar informar essa chave
no cdigo-fonte.
5 < & Apps Toofe: UvroArefroid Carros
o1 Googl e Ad5ense St al us: OIT
IncreMeyM* aef conM rata by eraMnj Gcote AdSense nyour po et t rs. Adrequests yrf xhcamot be fdtedfry be st vred w* h Goole AdSense tomath with
vadsle ad abcut usng Goote AdSerse h your acfcaben.
'ivroAndroid - Carros ' v
St el RL: nMri ceh/ / det si l s?i d=br>>T(undraLcarrt M
S^f cher ID: aHe8cf aSb8f 52f | Get Pubbher Co j ^ r
| AdFi ter* i| Settings [
^rr
SBe: | Li vroAndroi d - Canos y |
M a n a g e Fi l t e r Se t t i n g s cpy set tngs q
Co Vi j j s the set trgs bekw to corod Jx h ds to* * on your *4e. Note, Cf* ftner ffters ycj oeat r, (ha bctter Ad^eci mewre you revenue.
To v>c* ads Chat are dt e t o pear on ycw se, i se the Ad Searth looL
URL HReri j Tei t Fers Cat eay jTjpeSettJ igs | Language Sei tns ...... i . , Swdi Ads
Number of URL FUt en:
' LPL To F*a
Ab o u t UR L Fi l t e r s
j r . i ^ j r .17 !AddF3t br|
A URL fier btocfcs ads f the URL
you jpecfy matche* the URI to
i t f * h the ad W, Tha >ey, you
ear easy o* al ads Ht h f r*
t o a competJttr'* at e.
do publicador. Figura 12.12 - Detalhes da configurao da aplicao com a chave
12.9 Configurando o projeto
Neste momento j criamos uma conta no AdMobs e podemos ir para a parte
prtica.
Antes de exibir os anncios, precisamos configurar o projeto. Para isso basta
adicionar o .jar do Google AdMobs no classpath do projeto. A maneira recomendada
de fazer isso criar uma pasta lib no projeto e copiar o arquivo GoogleAdMobAdsSdk-
xxx.jar para essa pasta. Depois clique com o boto direito no arquivo e selecione o
menu Bui ld Path > AddT o Bui ld Path, e pronto. Lembre-se que o .jar do Google Analytics
j deve estar l.
A prxima configurao adicionar a acdvity do AdMobs no AndroidManifest.
xml da seguinte forma:
< activity android: name= " com. google. ads.AdActivity "
android:configChanges= " k ey board| k ey boardH idden| orientation' 7>
Cap tulo 12 Google An alyti cs e Google Mobi le Ads
427
Outra configurao necessria adicionar as permisses de internet e verificar
o estado da rede, que no aplicativo dos carros j foi configurado.
< uses-permission android:name= " android.permission. INTERNET" /)
< uses-permission android: name= " android. permission.ACCESS_ NETW O RK _ STATE" />
A seguir podemos verificar o arquivo completo do AndroidManifest.xml do
projeto dos carros.
#1 An droi dMan i f est.xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< manifest x mlns:android= " http://schemas.android.com/apk /res/android"
pack age= " br.livroandroid. carros'
android:versionCode= " l"
android:versionName= " 1.0" >
< uses-sdk android:minSdk Version= " 4" android:targetSdk Version= " ll" />
< uses-permission android:name= ' android.permission.INTERNET" />
< uses-permission android: name= ' android. permission.ACCESS_ NETW O RK _ STATE/>
< uses-permission android: name= " android. permission.W RITE_ EX TERNAL_ STO RAGE/>
< application android:name= " br.livroandroid.carros.CarrosApplication"
android: icon= " @ drawable/icon
android: label= " @ string/app_ name"
android:theme= " @ sty le/tema" >
< activity
android:label= " @ string/app_ name"
android:name= .Hain" >
ntent-filter >
< action android:naiue= " android.intent.action.HAIN" />
< category android:name= " android.intent.category .LAU NCH ER" />
</intent-filter>
<'/activity>
<! -- Smartphone -->
< activity android:name= .activity .TelaListaCarros" />
(activity android:name= " .activity .TelaDetalhesCarro" />
< activity android:name= " .activity .TelaSobre" />
<!-- Tablet -->
< activity android: name= " . activity . vil .TelalistaCarrosTablet" / >
<! -- AdM obs -->
< activity android: name= " com.google. ads .AdActivity "
android:configChanges=k ey board| k ey boardH idden| orientation" />
< /application>
< /manifest>
428
Google An droi d para T ablets
Depois de o projeto estar configurado s precisamos adicionar a view com.
google.ads.Adview em alguma parte da tela para exibir o anncio, o que faremos no
prximo tpico.
12.10 Como adicionar um anncio com o AdView
Adicionar os anncios fcil. Basta adicionar uma simples view na tela, que neste
caso a com.google.ads.Adview da biblioteca do AdMobs. Podemos inserir essa view
diretamente no XML ou criar a view dinamicamente pela API.
O Adview uma view comum, e basta ser adicionado em qualquer tela, conforme
este simples layout em XML.
& /res/!ayout/i n clude_admobs.xmi
< ? x ml version= " 1.0encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
x mlns:ads= " http://schemas.android.com/apk /lib/com. google.ads"
android:id="| S+id/l3y outAdM obs
android:orientation= " vertical"
android: layout_width=" fill_parent"
android:lay out_ height= " wrap_ content
android:lay out_ gravity = " bottom"
>
< com.google.ads.AdView android:id=" g+ id/adview"
android:lay out_ width= " wrap_ content"
android:lay out_ height= " wrap_ content"
ads:adU nitId= " al4e8efa5b8f52f"
ads:adSiz e= BANNER"
ads:loadAdO nCreate= " true" />
< /LinearLay out>
O parmetro ads:adSiz e= " BANNER" corresponde ao tamanho padro do banner,
onde a constante b a n n e r corresponde ao tamanho padro de 320 x 50 pixels para
smartphones.
O parmetro ads:adlinitid= " al4e8efa5b8f52f" a chave de publicador, a qual salva
mos anteriormente no site de administrao do AdMobs.
Em nosso exemplo utilizamos a configurao ads:adSiz e= " BANNER" porque estamos
preparando essa tela para smartphones com Android 2.x, onde o banner ser ren-
derizado com um tamanho de 320 x 50 pixels. Posteriormente vamos customizar
o anncio tambm para tablets. s
Cap tulo 12 Google An alyti cs e Google Mobi le Ads 429
O parmetro ads:adUnitId='al4eSefa5b8f52f deve ser substitudo pela
chave de editor, a qual possvel obter na administrao do site
AdMobs, conforme demonstrado anteriormente.
Se voc incluir esse layout XML em alguma tela da aplicao, os anncios j vo
funcionar. Mas para ter um maior controle sobre a forma como o AdView inserido
e tambm para controlar os parmetros da AdReq uest podemos optar por adicionar
essa view dinamicamente pela API, e para isso vamos criar essa classe utilitria.
f e AdMobsUti l.java
public class AdMobsLttil { :
private static final String ADD_ U NIT_ ID = "al4e8efa5b8f52f';
// Adiciona o AdView no lay out de uma Activity
public static void addAdView(Activity activity ) {
// Cria a view
AdView adView = new AdView(activity , AdSiz e.BANNER, ADD_U NIT_ID);
// Adiciona o AdView no layout
LinearLay out lay out = (LinearLay out) activity .findViewByld(R.id.layoutAdMobs);
lay out.addview(adview);
// Configura a req uisio
AdReq uest request = new AdReq uestQ ;
adView.loadAd(req uest);
}
}
Essa classe basicamente cria um AdView e o insere no layout de uma activity ou
fragment. Depois de inserir a view no layout criamos um objeto AdReq uest para
customizar a requisio dos anncios. Consulte a documentao para verificar
os parmetros que podem ser alterados.
Como pretendemos adicionar o AdView pela API, vamos alterar o arquivo de
XML para apenas declarar o layout que vai conter o anncio, o qual vamos inserir
dinamicamente pela activity ou fragment.
& /res/layout/i n dude_admobs.xml
< ? xml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns:android= " http://schemas.android.com/apk /res/android"
x mlns: ads= http ://schemas. android. com/apk /lib/com. google. ads"
android: id= " g+ id/lay outAdH obs
android: orientation='vertical"
android: layout_width="fill_parent''
android:lay out_ h ight= " wrap_ content"
android :layout_ gravity = " bottom'
android:lay out j nargin= 10dp"
>
<!-- AdView aq ui -->
< /LinearLay out>
12.11 Alterando os layouts das telas de carros -
Agora que j temos o arquivo /res/layout/include_admobs.xml que define um simples
layout para inserirmos o anncio e a classe AdM obsU til para inserir a AdView, vamos
alterar o layout da tela que lista os carros.
No layout que lista os carros da activity para smartphones basta inserirmos
um simples include conforme demonstrado a seguir,
nclude lay out= " @ lay out/include_ admobs" />
s /res/layout/carros.xml
< ? x ml version= " 1.0" encoding= " utf-8" ? >
< LinearLay out x mlns :android= " http://schemas. android .com/apk /res/android
android:orientation= " vertical"
android: layout_width="fill_parent"
android:lay out_ height= " fillj arent"
android:gravity = " center"
android:back ground= (3color/fundo"
>
nclude lay out= " @ lay out/include_ header" />
< LinearLay out
android: id= "|iW.d/layoutEsquerda"
android: lay out_ width= " fillj arent"
android: lay out_ height= " 0dp"
android:lay out_ weight= " l
>
<!-- F ragment aq ui -->
< /LinearLay out>
nclude lay out='' @layout/include_adn\obs" />
nclude lay out= " @ lay out/indude_ footer" />
< /LinearLay out>
'T -'u Google An droi d para T ablets
Cap tulo 12 Google An alyti cs e Google Mobi le Ads 431
I
Depois de inserir o include dos anncios necessrio acrescentar uma linha de
cdigo na activity para adicionar a Adview.
AdM obsU til.addAdView(this);
Na classe T e l a L i s t a C a r r o s vamos inseri r essa linha no final do mtodo
onCreate(bundle) conforme demonstrado a seguir.
T elaLi staCa rros.java
public class TelaListaCarros ex tends LivroAndroidActivity {
(SOverride
public void onCreate(Bundle savedlnstanceState) {
super.onCreate(savedlnstanceState);
setContentView(R.lay out.carros);
// No faz mais nada porq ue o F ragmentDetalhesCarro faz tudo
if (savedlnstanceState ~ null) {
// Insere somente a primeira vez
// Porq ue a F ragmentTransaction persiste durante a troca de orientao
F ragmentListaCarros fragLista = new F ragmentListaCarrosQ ;
// Passa os parmetros para o fragment, q ue cont m o tipo do carro selecionado
// Por isso estamos utiliz ando uma transao, pois pelo X ML no
// possvel passar os parmetros
Bundle args = getIntent().getEx tras();
fragLista.setArguments(args);
F ragmentTransaction t = getSupportF ragmentM anager().beginTransaction();
t .add(R.id.lay outEsq uerda, fragLista);
t.commit();
}
// AdView
AdM obsU til.addAdView(this);
}
}
E isso tudo! Feitas essas modificaes, j podemos executar o projeto dos
carros, e se tudo correr como o esperado, os anncios sero exibidos conforme
a figura 12.13.
432
Google An droi d para T ablets
UmborghiniAvenudor
Chevrolet Corvette 206
BMWM5
j j Renault Megane RS Trophy
MaseratfGrancabrio Sport
MCLAREN WP4-12C
MERCEDES-BENZ C63 AMG
Tablets no Magazine Luiza ,
TWjs *MfOatm at. 12<V Juros
Ajrow..-ttJ/inelul.
12.12 Verificando os logs
Uma dica importante que pode ajudar no desenvolvimento monitorar as men
sagens de logs do AdView. Para isso basta habilitar a tag " Ads" no LogCat.
Ao ativar os logs podemos visualizar as seguintes mensagens ao carregar algum
anncio:
(Lembre-se que em caso de erro possvel visualizar nessas mensagens infor
maes importantes para auxiliar a solucionar o problema.)
INF 0/Ads(1429): To get test ads on this device, call adReq uest.addTestDevice(" DEAC9133B27F 6D4AB
9A6DEA4DDD8718E" );
INF O /Ads(1429): adReq uestU rlH tml: < htmlx headx script src= ''http://www.gstatic.com/afma/sdk -
core-v40.j s" x /scriptx script> AF M A_ buildAdU RL({ " preq s" :2," u_ sd" :1.5," slotname" :" al4e8efa5b8f52f
", " u_ h" :533," u_ w" :320," msid" :" br.livroandroid.carros Vj s" :" afnia-sdk -a-v4.1.1" ," isu" :" DEAC9133B
27 F 6D4AB 9A6D EA4DDD8718 Ej " format" :" 320x 50_ mb" ," net" :" wi" ," appj iame" :" 1.android.br.livroandroid.
carros", u_ audio" :1," hl" :" en" } );< /script> &l